package fifo_stock_v2 import ( "context" "fmt" "strings" "gorm.io/gorm" ) type routeRule struct { ID uint `gorm:"column:id"` FlagGroupCode string `gorm:"column:flag_group_code"` Lane string `gorm:"column:lane"` FunctionCode string `gorm:"column:function_code"` SourceTable string `gorm:"column:source_table"` SourceIDColumn string `gorm:"column:source_id_column"` ProductWarehouseCol string `gorm:"column:product_warehouse_col"` QuantityCol string `gorm:"column:quantity_col"` UsedQuantityCol *string `gorm:"column:used_quantity_col"` PendingQuantityCol *string `gorm:"column:pending_quantity_col"` ScopeSQL *string `gorm:"column:scope_sql"` LegacyTypeKey string `gorm:"column:legacy_type_key"` AllowPendingDefault bool `gorm:"column:allow_pending_default"` } type traitRule struct { ID uint `gorm:"column:id"` SourceTable string `gorm:"column:source_table"` Lane string `gorm:"column:lane"` DateTable *string `gorm:"column:date_table"` DateJoinLeftCol *string `gorm:"column:date_join_left_col"` DateJoinRightCol *string `gorm:"column:date_join_right_col"` DateColumn string `gorm:"column:date_column"` FallbackDateColumn *string `gorm:"column:fallback_date_column"` SortPriority int `gorm:"column:sort_priority"` IDColumn string `gorm:"column:id_column"` } func (s *fifoStockV2Service) loadRouteRules(ctx context.Context, tx *gorm.DB, flagGroupCode string, lane Lane) ([]routeRule, error) { var rules []routeRule err := tx.WithContext(ctx). Table("fifo_stock_v2_route_rules"). Where("is_active = TRUE"). Where("flag_group_code = ?", flagGroupCode). Where("lane = ?", string(lane)). Order("id ASC"). Find(&rules).Error if err != nil { return nil, err } for _, rule := range rules { if err := validateRouteRule(rule); err != nil { return nil, err } } return rules, nil } func (s *fifoStockV2Service) loadRouteRuleByLegacyType( ctx context.Context, tx *gorm.DB, lane Lane, flagGroupCode string, legacyTypeKey string, ) (*routeRule, error) { var rule routeRule err := tx.WithContext(ctx). Table("fifo_stock_v2_route_rules"). Where("is_active = TRUE"). Where("lane = ?", string(lane)). Where("flag_group_code = ?", flagGroupCode). Where("legacy_type_key = ?", legacyTypeKey). Order("id ASC"). Limit(1). Take(&rule).Error if err != nil { return nil, err } if err := validateRouteRule(rule); err != nil { return nil, err } return &rule, nil } func (s *fifoStockV2Service) loadTraitMap( ctx context.Context, tx *gorm.DB, lane Lane, sourceTables []string, ) (map[string]traitRule, error) { if len(sourceTables) == 0 { return map[string]traitRule{}, nil } var traits []traitRule err := tx.WithContext(ctx). Table("fifo_stock_v2_traits"). Where("is_active = TRUE"). Where("lane = ?", string(lane)). Where("source_table IN ?", sourceTables). Find(&traits).Error if err != nil { return nil, err } out := make(map[string]traitRule, len(traits)) for _, tr := range traits { if err := validateTraitRule(tr); err != nil { return nil, err } out[tr.SourceTable] = tr } return out, nil } func validateRouteRule(rule routeRule) error { fields := []string{rule.SourceTable, rule.SourceIDColumn, rule.ProductWarehouseCol, rule.QuantityCol} for _, value := range fields { if _, err := mustSafeIdentifier(value); err != nil { return err } } if rule.UsedQuantityCol != nil { if _, err := mustSafeIdentifier(*rule.UsedQuantityCol); err != nil { return err } } if rule.PendingQuantityCol != nil { if _, err := mustSafeIdentifier(*rule.PendingQuantityCol); err != nil { return err } } if strings.TrimSpace(rule.LegacyTypeKey) == "" { return fmt.Errorf("route rule has empty legacy type key") } return nil } func validateTraitRule(rule traitRule) error { if _, err := mustSafeIdentifier(rule.SourceTable); err != nil { return err } if _, err := mustSafeIdentifier(rule.DateColumn); err != nil { return err } if _, err := mustSafeIdentifier(rule.IDColumn); err != nil { return err } if rule.DateTable != nil { if _, err := mustSafeIdentifier(*rule.DateTable); err != nil { return err } if rule.DateJoinLeftCol == nil || rule.DateJoinRightCol == nil { return fmt.Errorf("trait %s requires date join columns", rule.SourceTable) } if _, err := mustSafeIdentifier(*rule.DateJoinLeftCol); err != nil { return err } if _, err := mustSafeIdentifier(*rule.DateJoinRightCol); err != nil { return err } } if rule.FallbackDateColumn != nil { if _, err := mustSafeIdentifier(*rule.FallbackDateColumn); err != nil { return err } } return nil }