fix: chickin include stock allocation, fix calculation hpp

This commit is contained in:
Hafizh A. Y
2026-03-03 10:36:48 +07:00
parent d5a1751868
commit f6e25be76b
19 changed files with 665 additions and 59 deletions
+16 -15
View File
@@ -528,6 +528,7 @@ func (s *fifoService) allocateFromStock(
UsableType: usableKey.String(),
UsableId: usableID,
Qty: portion,
AllocationPurpose: entities.StockAllocationPurposeConsume,
Status: entities.StockAllocationStatusActive,
})
@@ -890,22 +891,22 @@ func (s *fifoService) fetchPendingCandidates(ctx context.Context, tx *gorm.DB, p
query = query.Order(order)
}
if err := query.Find(&rows).Error; err != nil {
return nil, err
}
for _, row := range rows {
if row.Pending <= 0 {
continue
if err := query.Find(&rows).Error; err != nil {
return nil, err
}
candidates = append(candidates, pendingCandidate{
UsableKey: key,
Config: cfg,
UsableID: row.ID,
Pending: row.Pending,
CreatedAt: time.Unix(0, row.CreatedAt),
})
}
} else {
for _, row := range rows {
if row.Pending <= 0 {
continue
}
candidates = append(candidates, pendingCandidate{
UsableKey: key,
Config: cfg,
UsableID: row.ID,
Pending: row.Pending,
CreatedAt: time.Unix(0, row.CreatedAt),
})
}
} else {
var rows []struct {
ID uint
Pending float64 `gorm:"column:pending_qty"`
@@ -157,6 +157,7 @@ func (s *fifoStockV2Service) allocateInternal(ctx context.Context, tx *gorm.DB,
"usable_id": req.Usable.ID,
"qty": portion,
"status": activeAllocationStatus(),
"allocation_purpose": defaultAllocationPurpose(),
"created_at": now,
"updated_at": now,
"engine_version": "v2",
@@ -591,7 +592,7 @@ func (s *fifoStockV2Service) loadActiveAllocations(
) ([]allocationRow, error) {
query := tx.Table("stock_allocations").
Select("id, product_warehouse_id, stockable_type, stockable_id, usable_type, usable_id, qty, status, created_at").
Where("usable_type = ? AND usable_id = ? AND status = ?", usableType, usableID, activeAllocationStatus())
Where("usable_type = ? AND usable_id = ? AND status = ? AND allocation_purpose = ?", usableType, usableID, activeAllocationStatus(), defaultAllocationPurpose())
if productWarehouseID > 0 {
query = query.Where("product_warehouse_id = ?", productWarehouseID)
}
@@ -690,6 +691,7 @@ func (s *fifoStockV2Service) resolveRollbackFlagGroup(ctx context.Context, tx *g
Select("flag_group_code").
Where("usable_type = ? AND usable_id = ?", req.Usable.LegacyTypeKey, req.Usable.ID).
Where("engine_version = 'v2'").
Where("allocation_purpose = ?", defaultAllocationPurpose()).
Where("flag_group_code IS NOT NULL AND flag_group_code <> ''").
Order("id DESC").
Limit(1).
@@ -48,6 +48,8 @@ func (s *fifoStockV2Service) Gather(ctx context.Context, req GatherRequest) ([]G
}
func (s *fifoStockV2Service) gatherRows(ctx context.Context, tx *gorm.DB, req GatherRequest) ([]GatherRow, error) {
req.AllocationPurpose = normalizeAllocationPurpose(req.AllocationPurpose)
rules, err := s.loadRouteRules(ctx, tx, req.FlagGroupCode, req.Lane)
if err != nil {
return nil, err
@@ -155,7 +157,7 @@ func (s *fifoStockV2Service) buildGatherSubquery(rule routeRule, trait traitRule
whereExtraArgs := make([]any, 0, 1)
if req.Lane == LaneStockable {
if rule.UsedQuantityCol != nil && strings.TrimSpace(*rule.UsedQuantityCol) != "" {
if !req.IgnoreSourceUsed && rule.UsedQuantityCol != nil && strings.TrimSpace(*rule.UsedQuantityCol) != "" {
usedCol, _ := mustSafeIdentifier(*rule.UsedQuantityCol)
usedExpr = fmt.Sprintf("COALESCE(src.%s,0)::numeric", usedCol)
} else {
@@ -167,13 +169,13 @@ func (s *fifoStockV2Service) buildGatherSubquery(rule routeRule, trait traitRule
// We split the args because the WHERE placeholder order appears
// after product/flag filter placeholders in the final SQL.
usedExpr = fmt.Sprintf(
"(SELECT COALESCE(SUM(sa.qty),0)::numeric FROM stock_allocations sa WHERE sa.stockable_type = ? AND sa.stockable_id = src.%s AND sa.status = '%s')",
"(SELECT COALESCE(SUM(sa.qty),0)::numeric FROM stock_allocations sa WHERE sa.stockable_type = ? AND sa.stockable_id = src.%s AND sa.status = '%s' AND sa.allocation_purpose = ?)",
sourceIDCol,
activeAllocationStatus(),
)
extraArgs = append(extraArgs, rule.LegacyTypeKey)
extraArgs = append(extraArgs, rule.LegacyTypeKey)
whereExtraArgs = append(whereExtraArgs, rule.LegacyTypeKey)
extraArgs = append(extraArgs, rule.LegacyTypeKey, req.AllocationPurpose)
extraArgs = append(extraArgs, rule.LegacyTypeKey, req.AllocationPurpose)
whereExtraArgs = append(whereExtraArgs, rule.LegacyTypeKey, req.AllocationPurpose)
}
availableExpr = fmt.Sprintf("(%s - %s)", baseQtyExpr, usedExpr)
} else {
@@ -238,7 +238,7 @@ func nearlyZero(v float64) bool {
}
func (s *fifoStockV2Service) ensureStockAllocationColumns(tx *gorm.DB) error {
checkCols := []string{"engine_version", "flag_group_code", "function_code", "idempotency_key"}
checkCols := []string{"engine_version", "flag_group_code", "function_code", "idempotency_key", "allocation_purpose"}
for _, col := range checkCols {
var count int64
err := tx.Raw(`
@@ -263,3 +263,15 @@ func activeAllocationStatus() string {
func releasedAllocationStatus() string {
return entity.StockAllocationStatusReleased
}
func defaultAllocationPurpose() string {
return entity.StockAllocationPurposeConsume
}
func normalizeAllocationPurpose(purpose string) string {
purpose = strings.TrimSpace(strings.ToUpper(purpose))
if purpose == "" {
return defaultAllocationPurpose()
}
return purpose
}
@@ -33,6 +33,8 @@ type Ref struct {
type GatherRequest struct {
FlagGroupCode string
Lane Lane
AllocationPurpose string
IgnoreSourceUsed bool
ProductWarehouseID uint
From *time.Time
AsOf *time.Time