205 lines
5.7 KiB
Go
205 lines
5.7 KiB
Go
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) {
|
|
pg := db.PgDB
|
|
mssql := db.MssqlDB
|
|
if mssql == nil {
|
|
// Some option fields can still be served from PG cache table.
|
|
if pg == 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]
|
|
}
|
|
|
|
// Fast path: use PG-derived pricing parameter cache for most fields.
|
|
// This avoids scanning ProductFilterWithDescription('TR') on MSSQL, which can be slow and cause 504s.
|
|
// productCode is not available in mk_urunpricingprmtr, keep MSSQL for that.
|
|
if pg != nil && field != "productCode" {
|
|
pgCol := ""
|
|
switch field {
|
|
case "brandGroupSelection":
|
|
pgCol = "brand_group_sec"
|
|
case "marka":
|
|
pgCol = "marka"
|
|
case "askiliYan":
|
|
pgCol = "askili_yan"
|
|
case "kategori":
|
|
pgCol = "kategori"
|
|
case "urunIlkGrubu":
|
|
pgCol = "urun_ilk_grubu"
|
|
case "urunAnaGrubu":
|
|
pgCol = "urun_ana_grubu"
|
|
case "urunAltGrubu":
|
|
pgCol = "urun_alt_grubu"
|
|
case "icerik":
|
|
pgCol = "icerik"
|
|
case "karisim":
|
|
// "karisim" is intentionally deprecated in mk_urunpricingprmtr, keep MSSQL fallback.
|
|
pgCol = ""
|
|
default:
|
|
pgCol = ""
|
|
}
|
|
|
|
if pgCol != "" {
|
|
args := make([]any, 0, 8)
|
|
where := []string{
|
|
"is_active = TRUE",
|
|
fmt.Sprintf("NULLIF(BTRIM(%s), '') IS NOT NULL", pgCol),
|
|
}
|
|
if len(scopeUrunIlkGrubu) > 0 && field != "urunIlkGrubu" {
|
|
args = append(args, scopeUrunIlkGrubu)
|
|
where = append(where, fmt.Sprintf("urun_ilk_grubu = ANY($%d::text[])", len(args)))
|
|
}
|
|
if q != "" {
|
|
like := q + "%"
|
|
args = append(args, like)
|
|
where = append(where, fmt.Sprintf("%s ILIKE $%d", pgCol, len(args)))
|
|
}
|
|
whereSQL := strings.Join(where, " AND ")
|
|
|
|
// Note: DISTINCT+ORDER+LIMIT is fine here due to small mk_urunpricingprmtr cardinality (~1000 rows).
|
|
sqlText := fmt.Sprintf(`
|
|
SELECT DISTINCT %s AS val
|
|
FROM mk_urunpricingprmtr
|
|
WHERE %s
|
|
ORDER BY val ASC
|
|
LIMIT %d
|
|
`, pgCol, whereSQL, limit)
|
|
|
|
rows, err := pg.QueryContext(ctx, sqlText, args...)
|
|
if err == nil {
|
|
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)
|
|
}
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
return out, nil
|
|
}
|
|
// If PG path fails, fall back to MSSQL below.
|
|
}
|
|
}
|
|
|
|
// 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()
|
|
}
|