package queries import ( "bssapp-backend/db" "context" "database/sql" "fmt" "strings" ) // GetProductPricingFilterOptions returns distinct option values for ProductPricing filters. // This is used to render filter dropdowns without loading the full dataset. func GetProductPricingFilterOptions(ctx context.Context, field string, q string, limit int, scopeUrunIlkGrubu []string) ([]string, error) { mssql := db.MssqlDB if mssql == nil { return nil, fmt.Errorf("mssql db is nil") } field = strings.TrimSpace(field) q = strings.TrimSpace(q) if limit <= 0 || limit > 200 { limit = 120 } if len(scopeUrunIlkGrubu) > 3 { scopeUrunIlkGrubu = scopeUrunIlkGrubu[:3] } // Map UI filter fields -> MSSQL expression in ProductFilterWithDescription('TR') var expr string switch field { case "productCode": expr = "LTRIM(RTRIM(ProductCode))" case "brandGroupSelection": expr = `CASE ABS(CHECKSUM(LTRIM(RTRIM(ProductCode)))) % 3 WHEN 0 THEN 'MARKA GRUBU A' WHEN 1 THEN 'MARKA GRUBU B' ELSE 'MARKA GRUBU C' END` case "marka": expr = "COALESCE(LTRIM(RTRIM(ProductAtt10Desc)), '')" case "askiliYan": expr = "COALESCE(LTRIM(RTRIM(ProductAtt45Desc)), '')" case "kategori": expr = "COALESCE(LTRIM(RTRIM(ProductAtt44Desc)), '')" case "urunIlkGrubu": expr = "COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '')" case "urunAnaGrubu": expr = "COALESCE(LTRIM(RTRIM(ProductAtt01Desc)), '')" case "urunAltGrubu": expr = "COALESCE(LTRIM(RTRIM(ProductAtt02Desc)), '')" case "icerik": expr = "COALESCE(LTRIM(RTRIM(ProductAtt41Desc)), '')" case "karisim": expr = "COALESCE(LTRIM(RTRIM(ProductAtt29Desc)), '')" default: return nil, fmt.Errorf("invalid field") } // NOTE: We keep the same base constraints as the listing query. // q: prefix match to keep it sargable-ish. args := make([]any, 0, 8) where := []string{ "ProductAtt42 IN ('SERI', 'AKSESUAR')", "IsBlocked = 0", "LEN(LTRIM(RTRIM(ProductCode))) = 13", } if len(scopeUrunIlkGrubu) > 0 && field != "urunIlkGrubu" { // Cascade scope: allow limiting options by the already selected "Urun Ilk Grubu" (desc). // We filter by desc value because UI uses desc fields. placeholders := make([]string, 0, len(scopeUrunIlkGrubu)) for _, v := range scopeUrunIlkGrubu { v = strings.TrimSpace(v) if v == "" { continue } placeholders = append(placeholders, fmt.Sprintf("@p%d", len(args)+1)) args = append(args, v) } if len(placeholders) > 0 { where = append(where, fmt.Sprintf("COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '') IN (%s)", strings.Join(placeholders, ", "))) } } if q != "" { // For productCode, allow contains if user types middle; for others use prefix. if field == "productCode" { where = append(where, expr+fmt.Sprintf(" LIKE @p%d", len(args)+1)) args = append(args, "%"+q+"%") } else { where = append(where, expr+fmt.Sprintf(" LIKE @p%d", len(args)+1)) args = append(args, q+"%") } } whereSQL := strings.Join(where, " AND ") sqlText := fmt.Sprintf(` SELECT TOP (%d) X.val FROM ( SELECT DISTINCT NULLIF(%s, '') AS val FROM ProductFilterWithDescription('TR') WHERE %s ) X WHERE X.val IS NOT NULL ORDER BY X.val ASC; `, limit, expr, whereSQL) rows, err := mssql.QueryContext(ctx, sqlText, args...) if err != nil { return nil, err } defer rows.Close() out := make([]string, 0, limit) for rows.Next() { var v sql.NullString if err := rows.Scan(&v); err != nil { return nil, err } if s := strings.TrimSpace(v.String); s != "" { out = append(out, s) } } return out, rows.Err() }