950 lines
33 KiB
Go
950 lines
33 KiB
Go
package queries
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/lib/pq"
|
|
)
|
|
|
|
// Rule tables:
|
|
// - mk_pricing_rule: the "scope" (filters) to which multipliers/roundings apply.
|
|
// - mk_pricex: per-currency multipliers (base + 1..6).
|
|
// - mk_priceroll: per-currency rounding steps for wholesale (1-5) and retail (6+).
|
|
|
|
func normalizeRetailMode(v string) string {
|
|
v = strings.ToUpper(strings.TrimSpace(v))
|
|
switch v {
|
|
case "", "STEP":
|
|
return "STEP"
|
|
case "END_99", "END_49", "BAND_99", "BAND_49":
|
|
return v
|
|
default:
|
|
return "STEP"
|
|
}
|
|
}
|
|
|
|
func NormalizeRetailModeForRoute(v string) string {
|
|
return normalizeRetailMode(v)
|
|
}
|
|
|
|
func EnsurePricingRuleTables(pg *sql.DB) error {
|
|
stmts := []string{
|
|
`
|
|
CREATE TABLE IF NOT EXISTS mk_pricing_rule (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
|
|
askili_yan TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
kategori TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
urun_ilk_grubu TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
urun_ana_grubu TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
urun_alt_grubu TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
icerik TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
karisim TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
marka TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
brand_code TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
brand_group TEXT[] NOT NULL DEFAULT '{}'::text[],
|
|
|
|
strategy_code TEXT NOT NULL DEFAULT 'CORE',
|
|
anchor_mode TEXT NOT NULL DEFAULT 'USD',
|
|
calc_enabled BOOLEAN NOT NULL DEFAULT TRUE,
|
|
publish_postgres BOOLEAN NOT NULL DEFAULT TRUE,
|
|
publish_nebim BOOLEAN NOT NULL DEFAULT TRUE,
|
|
is_active BOOLEAN NOT NULL DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
)`,
|
|
`ALTER TABLE mk_pricing_rule ADD COLUMN IF NOT EXISTS strategy_code TEXT NOT NULL DEFAULT 'CORE'`,
|
|
`ALTER TABLE mk_pricing_rule ADD COLUMN IF NOT EXISTS anchor_mode TEXT NOT NULL DEFAULT 'USD'`,
|
|
`ALTER TABLE mk_pricing_rule ADD COLUMN IF NOT EXISTS calc_enabled BOOLEAN NOT NULL DEFAULT TRUE`,
|
|
`ALTER TABLE mk_pricing_rule ADD COLUMN IF NOT EXISTS publish_postgres BOOLEAN NOT NULL DEFAULT TRUE`,
|
|
`ALTER TABLE mk_pricing_rule ADD COLUMN IF NOT EXISTS publish_nebim BOOLEAN NOT NULL DEFAULT TRUE`,
|
|
`UPDATE mk_pricing_rule SET strategy_code='CORE' WHERE COALESCE(NULLIF(BTRIM(strategy_code), ''), '') = ''`,
|
|
`UPDATE mk_pricing_rule SET anchor_mode='USD' WHERE COALESCE(NULLIF(BTRIM(anchor_mode), ''), '') = ''`,
|
|
`ALTER TABLE mk_pricing_rule DROP CONSTRAINT IF EXISTS ck_mk_pricing_rule_strategy_code`,
|
|
`ALTER TABLE mk_pricing_rule ADD CONSTRAINT ck_mk_pricing_rule_strategy_code CHECK (strategy_code IN ('CORE','PREMIUM','SARTORIAL'))`,
|
|
`ALTER TABLE mk_pricing_rule DROP CONSTRAINT IF EXISTS ck_mk_pricing_rule_anchor_mode`,
|
|
`ALTER TABLE mk_pricing_rule ADD CONSTRAINT ck_mk_pricing_rule_anchor_mode CHECK (anchor_mode IN ('TRY','USD'))`,
|
|
`CREATE INDEX IF NOT EXISTS ix_mk_pricing_rule_active ON mk_pricing_rule (is_active)`,
|
|
`
|
|
CREATE TABLE IF NOT EXISTS mk_pricex (
|
|
rule_id UUID NOT NULL REFERENCES mk_pricing_rule(id) ON DELETE CASCADE,
|
|
currency TEXT NOT NULL CHECK (currency IN ('TRY','USD','EUR')),
|
|
base_mult NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m1 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m2 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m3 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m4 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m5 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
m6 NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (rule_id, currency)
|
|
)`,
|
|
`CREATE INDEX IF NOT EXISTS ix_mk_pricex_currency ON mk_pricex (currency)`,
|
|
`
|
|
CREATE TABLE IF NOT EXISTS mk_priceroll (
|
|
rule_id UUID NOT NULL REFERENCES mk_pricing_rule(id) ON DELETE CASCADE,
|
|
currency TEXT NOT NULL CHECK (currency IN ('TRY','USD','EUR')),
|
|
step NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
wholesale_step NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
retail_step NUMERIC(18,6) NOT NULL DEFAULT 0,
|
|
retail_mode TEXT NOT NULL DEFAULT 'STEP',
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (rule_id, currency)
|
|
)`,
|
|
`ALTER TABLE mk_priceroll ADD COLUMN IF NOT EXISTS wholesale_step NUMERIC(18,6) NOT NULL DEFAULT 0`,
|
|
`ALTER TABLE mk_priceroll ADD COLUMN IF NOT EXISTS retail_step NUMERIC(18,6) NOT NULL DEFAULT 0`,
|
|
`ALTER TABLE mk_priceroll ADD COLUMN IF NOT EXISTS retail_mode TEXT NOT NULL DEFAULT 'STEP'`,
|
|
`UPDATE mk_priceroll SET wholesale_step = step, retail_step = step WHERE step <> 0 AND wholesale_step = 0 AND retail_step = 0`,
|
|
`UPDATE mk_priceroll SET retail_mode='STEP' WHERE COALESCE(NULLIF(BTRIM(retail_mode), ''), '') = ''`,
|
|
`CREATE INDEX IF NOT EXISTS ix_mk_priceroll_currency ON mk_priceroll (currency)`,
|
|
}
|
|
for _, s := range stmts {
|
|
if _, err := pg.Exec(s); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type PricingRuleRow struct {
|
|
ID string `json:"id"`
|
|
PricingParameterID int64 `json:"pricing_parameter_id"`
|
|
|
|
AskiliYan []string `json:"askili_yan"`
|
|
Kategori []string `json:"kategori"`
|
|
UrunIlkGrubu []string `json:"urun_ilk_grubu"`
|
|
UrunAnaGrubu []string `json:"urun_ana_grubu"`
|
|
UrunAltGrubu []string `json:"urun_alt_grubu"`
|
|
Icerik []string `json:"icerik"`
|
|
Karisim []string `json:"karisim"`
|
|
Marka []string `json:"marka"`
|
|
BrandCode []string `json:"brand_code"`
|
|
BrandGroupSec []string `json:"brand_group"`
|
|
|
|
StrategyCode string `json:"strategy_code"`
|
|
AnchorMode string `json:"anchor_mode"`
|
|
CalcEnabled bool `json:"calc_enabled"`
|
|
PublishPostgres bool `json:"publish_postgres"`
|
|
PublishNebim bool `json:"publish_nebim"`
|
|
IsActive bool `json:"is_active"`
|
|
|
|
// multipliers/rolls are per currency
|
|
TryBase float64 `json:"try_base"`
|
|
Try1 float64 `json:"try1"`
|
|
Try2 float64 `json:"try2"`
|
|
Try3 float64 `json:"try3"`
|
|
Try4 float64 `json:"try4"`
|
|
Try5 float64 `json:"try5"`
|
|
Try6 float64 `json:"try6"`
|
|
TryWholesaleStep float64 `json:"try_wholesale_step"`
|
|
TryRetailStep float64 `json:"try_retail_step"`
|
|
TryRetailMode string `json:"try_retail_mode"`
|
|
|
|
UsdBase float64 `json:"usd_base"`
|
|
Usd1 float64 `json:"usd1"`
|
|
Usd2 float64 `json:"usd2"`
|
|
Usd3 float64 `json:"usd3"`
|
|
Usd4 float64 `json:"usd4"`
|
|
Usd5 float64 `json:"usd5"`
|
|
Usd6 float64 `json:"usd6"`
|
|
UsdWholesaleStep float64 `json:"usd_wholesale_step"`
|
|
UsdRetailStep float64 `json:"usd_retail_step"`
|
|
UsdRetailMode string `json:"usd_retail_mode"`
|
|
|
|
EurBase float64 `json:"eur_base"`
|
|
Eur1 float64 `json:"eur1"`
|
|
Eur2 float64 `json:"eur2"`
|
|
Eur3 float64 `json:"eur3"`
|
|
Eur4 float64 `json:"eur4"`
|
|
Eur5 float64 `json:"eur5"`
|
|
Eur6 float64 `json:"eur6"`
|
|
EurWholesaleStep float64 `json:"eur_wholesale_step"`
|
|
EurRetailStep float64 `json:"eur_retail_step"`
|
|
EurRetailMode string `json:"eur_retail_mode"`
|
|
}
|
|
|
|
type PricingRuleSaveItem struct {
|
|
ID string `json:"id"`
|
|
PricingParameterID int64 `json:"pricing_parameter_id"`
|
|
|
|
AskiliYan []string `json:"askili_yan"`
|
|
Kategori []string `json:"kategori"`
|
|
UrunIlkGrubu []string `json:"urun_ilk_grubu"`
|
|
UrunAnaGrubu []string `json:"urun_ana_grubu"`
|
|
UrunAltGrubu []string `json:"urun_alt_grubu"`
|
|
Icerik []string `json:"icerik"`
|
|
Karisim []string `json:"karisim"`
|
|
Marka []string `json:"marka"`
|
|
BrandCode []string `json:"brand_code"`
|
|
BrandGroupSec []string `json:"brand_group"`
|
|
|
|
StrategyCode string `json:"strategy_code"`
|
|
AnchorMode string `json:"anchor_mode"`
|
|
CalcEnabled bool `json:"calc_enabled"`
|
|
PublishPostgres bool `json:"publish_postgres"`
|
|
PublishNebim bool `json:"publish_nebim"`
|
|
IsActive bool `json:"is_active"`
|
|
|
|
TryBase float64 `json:"try_base"`
|
|
Try1 float64 `json:"try1"`
|
|
Try2 float64 `json:"try2"`
|
|
Try3 float64 `json:"try3"`
|
|
Try4 float64 `json:"try4"`
|
|
Try5 float64 `json:"try5"`
|
|
Try6 float64 `json:"try6"`
|
|
TryWholesaleStep float64 `json:"try_wholesale_step"`
|
|
TryRetailStep float64 `json:"try_retail_step"`
|
|
TryRetailMode string `json:"try_retail_mode"`
|
|
|
|
UsdBase float64 `json:"usd_base"`
|
|
Usd1 float64 `json:"usd1"`
|
|
Usd2 float64 `json:"usd2"`
|
|
Usd3 float64 `json:"usd3"`
|
|
Usd4 float64 `json:"usd4"`
|
|
Usd5 float64 `json:"usd5"`
|
|
Usd6 float64 `json:"usd6"`
|
|
UsdWholesaleStep float64 `json:"usd_wholesale_step"`
|
|
UsdRetailStep float64 `json:"usd_retail_step"`
|
|
UsdRetailMode string `json:"usd_retail_mode"`
|
|
|
|
EurBase float64 `json:"eur_base"`
|
|
Eur1 float64 `json:"eur1"`
|
|
Eur2 float64 `json:"eur2"`
|
|
Eur3 float64 `json:"eur3"`
|
|
Eur4 float64 `json:"eur4"`
|
|
Eur5 float64 `json:"eur5"`
|
|
Eur6 float64 `json:"eur6"`
|
|
EurWholesaleStep float64 `json:"eur_wholesale_step"`
|
|
EurRetailStep float64 `json:"eur_retail_step"`
|
|
EurRetailMode string `json:"eur_retail_mode"`
|
|
}
|
|
|
|
// BulkSavePricingRulesFast persists multipliers + rounding steps in a set-based way.
|
|
// This is intentionally "dumb": it updates/creates a mk_pricing_rule row (latest by pricing_parameter_id)
|
|
// and upserts mk_pricex/mk_priceroll for TRY/USD/EUR.
|
|
func BulkSavePricingRulesFast(ctx context.Context, tx *sql.Tx, items []PricingRuleSaveItem) (int, error) {
|
|
if len(items) == 0 {
|
|
return 0, nil
|
|
}
|
|
|
|
raw, err := json.Marshal(items)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Notes:
|
|
// - rule_id resolution:
|
|
// 1) explicit id (if provided)
|
|
// 2) latest rule for pricing_parameter_id (if provided)
|
|
// 3) otherwise new UUID
|
|
// - mk_pricing_rule has no unique constraint on pricing_parameter_id by design, so we target "latest" row.
|
|
// - created_at uses default; updated_at is bumped on every save.
|
|
q := `
|
|
WITH input AS (
|
|
SELECT *
|
|
FROM jsonb_to_recordset($1::jsonb) AS x(
|
|
id text,
|
|
pricing_parameter_id bigint,
|
|
calc_enabled boolean,
|
|
publish_postgres boolean,
|
|
publish_nebim boolean,
|
|
is_active boolean,
|
|
try_retail_mode text,
|
|
usd_retail_mode text,
|
|
eur_retail_mode text,
|
|
|
|
try_base float8, try1 float8, try2 float8, try3 float8, try4 float8, try5 float8, try6 float8,
|
|
try_wholesale_step float8, try_retail_step float8,
|
|
|
|
usd_base float8, usd1 float8, usd2 float8, usd3 float8, usd4 float8, usd5 float8, usd6 float8,
|
|
usd_wholesale_step float8, usd_retail_step float8,
|
|
|
|
eur_base float8, eur1 float8, eur2 float8, eur3 float8, eur4 float8, eur5 float8, eur6 float8,
|
|
eur_wholesale_step float8, eur_retail_step float8
|
|
)
|
|
),
|
|
norm AS (
|
|
SELECT
|
|
NULLIF(BTRIM(id), '') AS id_txt,
|
|
COALESCE(pricing_parameter_id, 0) AS pricing_parameter_id,
|
|
COALESCE(calc_enabled, TRUE) AS calc_enabled,
|
|
COALESCE(publish_postgres, TRUE) AS publish_postgres,
|
|
COALESCE(publish_nebim, TRUE) AS publish_nebim,
|
|
COALESCE(is_active, TRUE) AS is_active,
|
|
COALESCE(NULLIF(UPPER(BTRIM(try_retail_mode)), ''), 'STEP') AS try_retail_mode,
|
|
COALESCE(NULLIF(UPPER(BTRIM(usd_retail_mode)), ''), 'STEP') AS usd_retail_mode,
|
|
COALESCE(NULLIF(UPPER(BTRIM(eur_retail_mode)), ''), 'STEP') AS eur_retail_mode,
|
|
|
|
COALESCE(try_base, 0) AS try_base, COALESCE(try1, 0) AS try1, COALESCE(try2, 0) AS try2, COALESCE(try3, 0) AS try3, COALESCE(try4, 0) AS try4, COALESCE(try5, 0) AS try5, COALESCE(try6, 0) AS try6,
|
|
COALESCE(try_wholesale_step, 0) AS try_wholesale_step, COALESCE(try_retail_step, 0) AS try_retail_step,
|
|
|
|
COALESCE(usd_base, 0) AS usd_base, COALESCE(usd1, 0) AS usd1, COALESCE(usd2, 0) AS usd2, COALESCE(usd3, 0) AS usd3, COALESCE(usd4, 0) AS usd4, COALESCE(usd5, 0) AS usd5, COALESCE(usd6, 0) AS usd6,
|
|
COALESCE(usd_wholesale_step, 0) AS usd_wholesale_step, COALESCE(usd_retail_step, 0) AS usd_retail_step,
|
|
|
|
COALESCE(eur_base, 0) AS eur_base, COALESCE(eur1, 0) AS eur1, COALESCE(eur2, 0) AS eur2, COALESCE(eur3, 0) AS eur3, COALESCE(eur4, 0) AS eur4, COALESCE(eur5, 0) AS eur5, COALESCE(eur6, 0) AS eur6,
|
|
COALESCE(eur_wholesale_step, 0) AS eur_wholesale_step, COALESCE(eur_retail_step, 0) AS eur_retail_step
|
|
FROM input
|
|
),
|
|
resolved AS (
|
|
SELECT
|
|
COALESCE(
|
|
NULLIF(id_txt, '')::uuid,
|
|
latest.id,
|
|
gen_random_uuid()
|
|
) AS rule_id,
|
|
pricing_parameter_id,
|
|
calc_enabled,
|
|
publish_postgres,
|
|
publish_nebim,
|
|
is_active,
|
|
try_retail_mode,
|
|
usd_retail_mode,
|
|
eur_retail_mode,
|
|
|
|
try_base, try1, try2, try3, try4, try5, try6,
|
|
try_wholesale_step, try_retail_step,
|
|
usd_base, usd1, usd2, usd3, usd4, usd5, usd6,
|
|
usd_wholesale_step, usd_retail_step,
|
|
eur_base, eur1, eur2, eur3, eur4, eur5, eur6,
|
|
eur_wholesale_step, eur_retail_step
|
|
FROM norm n
|
|
LEFT JOIN LATERAL (
|
|
SELECT r.id
|
|
FROM mk_pricing_rule r
|
|
WHERE r.pricing_parameter_id = n.pricing_parameter_id
|
|
ORDER BY r.created_at DESC, r.updated_at DESC, r.id DESC
|
|
LIMIT 1
|
|
) latest ON (n.id_txt IS NULL AND n.pricing_parameter_id > 0)
|
|
),
|
|
upsert_rule AS (
|
|
INSERT INTO mk_pricing_rule (
|
|
id,
|
|
pricing_parameter_id,
|
|
calc_enabled,
|
|
publish_postgres,
|
|
publish_nebim,
|
|
is_active,
|
|
updated_at
|
|
)
|
|
SELECT
|
|
rule_id,
|
|
NULLIF(pricing_parameter_id, 0),
|
|
calc_enabled,
|
|
publish_postgres,
|
|
publish_nebim,
|
|
is_active,
|
|
now()
|
|
FROM resolved
|
|
ON CONFLICT (id) DO UPDATE SET
|
|
pricing_parameter_id = EXCLUDED.pricing_parameter_id,
|
|
calc_enabled = EXCLUDED.calc_enabled,
|
|
publish_postgres = EXCLUDED.publish_postgres,
|
|
publish_nebim = EXCLUDED.publish_nebim,
|
|
is_active = EXCLUDED.is_active,
|
|
updated_at = now()
|
|
RETURNING id
|
|
),
|
|
upsert_pricex AS (
|
|
INSERT INTO mk_pricex (rule_id, currency, base_mult, m1, m2, m3, m4, m5, m6, updated_at)
|
|
SELECT rule_id, 'TRY', try_base, try1, try2, try3, try4, try5, try6, now() FROM resolved
|
|
UNION ALL
|
|
SELECT rule_id, 'USD', usd_base, usd1, usd2, usd3, usd4, usd5, usd6, now() FROM resolved
|
|
UNION ALL
|
|
SELECT rule_id, 'EUR', eur_base, eur1, eur2, eur3, eur4, eur5, eur6, now() FROM resolved
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
base_mult = EXCLUDED.base_mult,
|
|
m1 = EXCLUDED.m1,
|
|
m2 = EXCLUDED.m2,
|
|
m3 = EXCLUDED.m3,
|
|
m4 = EXCLUDED.m4,
|
|
m5 = EXCLUDED.m5,
|
|
m6 = EXCLUDED.m6,
|
|
updated_at = now()
|
|
RETURNING 1
|
|
),
|
|
upsert_priceroll AS (
|
|
INSERT INTO mk_priceroll (rule_id, currency, wholesale_step, retail_step, retail_mode, updated_at)
|
|
SELECT rule_id, 'TRY', try_wholesale_step, try_retail_step, try_retail_mode, now() FROM resolved
|
|
UNION ALL
|
|
SELECT rule_id, 'USD', usd_wholesale_step, usd_retail_step, usd_retail_mode, now() FROM resolved
|
|
UNION ALL
|
|
SELECT rule_id, 'EUR', eur_wholesale_step, eur_retail_step, eur_retail_mode, now() FROM resolved
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
wholesale_step = EXCLUDED.wholesale_step,
|
|
retail_step = EXCLUDED.retail_step,
|
|
retail_mode = EXCLUDED.retail_mode,
|
|
updated_at = now()
|
|
RETURNING 1
|
|
)
|
|
SELECT COUNT(*)::int FROM resolved;
|
|
`
|
|
|
|
var updated int
|
|
if err := tx.QueryRowContext(ctx, q, raw).Scan(&updated); err != nil {
|
|
return 0, err
|
|
}
|
|
return updated, nil
|
|
}
|
|
|
|
func ListPricingRules(ctx context.Context, pg *sql.DB) ([]PricingRuleRow, error) {
|
|
// Use LEFT joins so newly inserted rules show defaults.
|
|
q := `
|
|
SELECT
|
|
r.id,
|
|
COALESCE(r.pricing_parameter_id, 0),
|
|
r.askili_yan,
|
|
r.kategori,
|
|
r.urun_ilk_grubu,
|
|
r.urun_ana_grubu,
|
|
r.urun_alt_grubu,
|
|
r.icerik,
|
|
r.karisim,
|
|
r.marka,
|
|
r.brand_code,
|
|
r.brand_group,
|
|
r.strategy_code,
|
|
r.anchor_mode,
|
|
r.calc_enabled,
|
|
r.publish_postgres,
|
|
r.publish_nebim,
|
|
r.is_active,
|
|
|
|
COALESCE(tx.base_mult, 0)::float8 AS try_base,
|
|
COALESCE(tx.m1, 0)::float8 AS try1,
|
|
COALESCE(tx.m2, 0)::float8 AS try2,
|
|
COALESCE(tx.m3, 0)::float8 AS try3,
|
|
COALESCE(tx.m4, 0)::float8 AS try4,
|
|
COALESCE(tx.m5, 0)::float8 AS try5,
|
|
COALESCE(tx.m6, 0)::float8 AS try6,
|
|
COALESCE(NULLIF(tr.wholesale_step, 0), tr.step, 0)::float8 AS try_wholesale_step,
|
|
COALESCE(NULLIF(tr.retail_step, 0), tr.step, 0)::float8 AS try_retail_step,
|
|
COALESCE(NULLIF(BTRIM(tr.retail_mode), ''), 'STEP') AS try_retail_mode,
|
|
|
|
COALESCE(ux.base_mult, 0)::float8 AS usd_base,
|
|
COALESCE(ux.m1, 0)::float8 AS usd1,
|
|
COALESCE(ux.m2, 0)::float8 AS usd2,
|
|
COALESCE(ux.m3, 0)::float8 AS usd3,
|
|
COALESCE(ux.m4, 0)::float8 AS usd4,
|
|
COALESCE(ux.m5, 0)::float8 AS usd5,
|
|
COALESCE(ux.m6, 0)::float8 AS usd6,
|
|
COALESCE(NULLIF(ur.wholesale_step, 0), ur.step, 0)::float8 AS usd_wholesale_step,
|
|
COALESCE(NULLIF(ur.retail_step, 0), ur.step, 0)::float8 AS usd_retail_step,
|
|
COALESCE(NULLIF(BTRIM(ur.retail_mode), ''), 'STEP') AS usd_retail_mode,
|
|
|
|
COALESCE(ex.base_mult, 0)::float8 AS eur_base,
|
|
COALESCE(ex.m1, 0)::float8 AS eur1,
|
|
COALESCE(ex.m2, 0)::float8 AS eur2,
|
|
COALESCE(ex.m3, 0)::float8 AS eur3,
|
|
COALESCE(ex.m4, 0)::float8 AS eur4,
|
|
COALESCE(ex.m5, 0)::float8 AS eur5,
|
|
COALESCE(ex.m6, 0)::float8 AS eur6,
|
|
COALESCE(NULLIF(er.wholesale_step, 0), er.step, 0)::float8 AS eur_wholesale_step,
|
|
COALESCE(NULLIF(er.retail_step, 0), er.step, 0)::float8 AS eur_retail_step,
|
|
COALESCE(NULLIF(BTRIM(er.retail_mode), ''), 'STEP') AS eur_retail_mode
|
|
FROM mk_pricing_rule r
|
|
LEFT JOIN mk_pricex tx ON tx.rule_id = r.id AND tx.currency='TRY'
|
|
LEFT JOIN mk_pricex ux ON ux.rule_id = r.id AND ux.currency='USD'
|
|
LEFT JOIN mk_pricex ex ON ex.rule_id = r.id AND ex.currency='EUR'
|
|
LEFT JOIN mk_priceroll tr ON tr.rule_id = r.id AND tr.currency='TRY'
|
|
LEFT JOIN mk_priceroll ur ON ur.rule_id = r.id AND ur.currency='USD'
|
|
LEFT JOIN mk_priceroll er ON er.rule_id = r.id AND er.currency='EUR'
|
|
ORDER BY r.created_at DESC;
|
|
`
|
|
rows, err := pg.QueryContext(ctx, q)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
out := make([]PricingRuleRow, 0, 256)
|
|
for rows.Next() {
|
|
var r PricingRuleRow
|
|
if err := rows.Scan(
|
|
&r.ID,
|
|
&r.PricingParameterID,
|
|
pq.Array(&r.AskiliYan),
|
|
pq.Array(&r.Kategori),
|
|
pq.Array(&r.UrunIlkGrubu),
|
|
pq.Array(&r.UrunAnaGrubu),
|
|
pq.Array(&r.UrunAltGrubu),
|
|
pq.Array(&r.Icerik),
|
|
pq.Array(&r.Karisim),
|
|
pq.Array(&r.Marka),
|
|
pq.Array(&r.BrandCode),
|
|
pq.Array(&r.BrandGroupSec),
|
|
&r.StrategyCode,
|
|
&r.AnchorMode,
|
|
&r.CalcEnabled,
|
|
&r.PublishPostgres,
|
|
&r.PublishNebim,
|
|
&r.IsActive,
|
|
|
|
&r.TryBase, &r.Try1, &r.Try2, &r.Try3, &r.Try4, &r.Try5, &r.Try6, &r.TryWholesaleStep, &r.TryRetailStep, &r.TryRetailMode,
|
|
&r.UsdBase, &r.Usd1, &r.Usd2, &r.Usd3, &r.Usd4, &r.Usd5, &r.Usd6, &r.UsdWholesaleStep, &r.UsdRetailStep, &r.UsdRetailMode,
|
|
&r.EurBase, &r.Eur1, &r.Eur2, &r.Eur3, &r.Eur4, &r.Eur5, &r.Eur6, &r.EurWholesaleStep, &r.EurRetailStep, &r.EurRetailMode,
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
r.TryRetailMode = normalizeRetailMode(r.TryRetailMode)
|
|
r.UsdRetailMode = normalizeRetailMode(r.UsdRetailMode)
|
|
r.EurRetailMode = normalizeRetailMode(r.EurRetailMode)
|
|
out = append(out, r)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func normalizeTextList(in []string) []string {
|
|
out := make([]string, 0, len(in))
|
|
seen := map[string]struct{}{}
|
|
for _, v := range in {
|
|
v = strings.TrimSpace(v)
|
|
if v == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[v]; ok {
|
|
continue
|
|
}
|
|
seen[v] = struct{}{}
|
|
out = append(out, v)
|
|
}
|
|
return out
|
|
}
|
|
|
|
func deriveStrategyCodeFromBrandGroup(values []string) string {
|
|
for _, value := range values {
|
|
normalized := strings.ToUpper(strings.TrimSpace(value))
|
|
switch normalized {
|
|
case "CORE", "PREMIUM", "SARTORIAL":
|
|
return normalized
|
|
}
|
|
}
|
|
return "CORE"
|
|
}
|
|
|
|
func deriveAnchorModeFromBrandGroup(ctx context.Context, tx *sql.Tx, values []string) string {
|
|
for _, value := range values {
|
|
normalized := strings.TrimSpace(value)
|
|
if normalized == "" {
|
|
continue
|
|
}
|
|
var mode string
|
|
err := tx.QueryRowContext(ctx, `
|
|
SELECT anchor_mode
|
|
FROM mk_brandgrp
|
|
WHERE UPPER(BTRIM(code)) = UPPER(BTRIM($1))
|
|
OR UPPER(BTRIM(title)) = UPPER(BTRIM($1))
|
|
ORDER BY id
|
|
LIMIT 1
|
|
`, normalized).Scan(&mode)
|
|
if err == nil {
|
|
mode = strings.ToUpper(strings.TrimSpace(mode))
|
|
if mode == "TRY" || mode == "USD" {
|
|
return mode
|
|
}
|
|
}
|
|
}
|
|
return "USD"
|
|
}
|
|
|
|
// UpsertPricingRule persists rule scope + per-currency multipliers/roundings.
|
|
// Parameter-backed worksheet saves append a new rule version so older prices
|
|
// remain queryable. Legacy rules without a parameter id keep update behavior.
|
|
func UpsertPricingRule(ctx context.Context, tx *sql.Tx, item PricingRuleSaveItem) (string, error) {
|
|
if item.PricingParameterID > 0 {
|
|
versionedParameterID, err := VersionPricingParameterForRule(ctx, tx, item.PricingParameterID)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
item.PricingParameterID = versionedParameterID
|
|
}
|
|
if err := FillPricingRuleScopeFromParameter(ctx, tx, &item); err != nil {
|
|
return "", err
|
|
}
|
|
item.AskiliYan = normalizeTextList(item.AskiliYan)
|
|
item.Kategori = normalizeTextList(item.Kategori)
|
|
item.UrunIlkGrubu = normalizeTextList(item.UrunIlkGrubu)
|
|
item.UrunAnaGrubu = normalizeTextList(item.UrunAnaGrubu)
|
|
item.UrunAltGrubu = normalizeTextList(item.UrunAltGrubu)
|
|
item.Icerik = normalizeTextList(item.Icerik)
|
|
item.Karisim = normalizeTextList(item.Karisim)
|
|
item.Marka = normalizeTextList(item.Marka)
|
|
item.BrandCode = normalizeTextList(item.BrandCode)
|
|
item.BrandGroupSec = normalizeTextList(item.BrandGroupSec)
|
|
item.StrategyCode = deriveStrategyCodeFromBrandGroup(item.BrandGroupSec)
|
|
item.AnchorMode = deriveAnchorModeFromBrandGroup(ctx, tx, item.BrandGroupSec)
|
|
item.TryRetailMode = normalizeRetailMode(item.TryRetailMode)
|
|
item.UsdRetailMode = normalizeRetailMode(item.UsdRetailMode)
|
|
item.EurRetailMode = normalizeRetailMode(item.EurRetailMode)
|
|
|
|
id := strings.TrimSpace(item.ID)
|
|
if item.PricingParameterID > 0 {
|
|
id = ""
|
|
}
|
|
if id == "" {
|
|
// create
|
|
if err := tx.QueryRowContext(ctx, `
|
|
INSERT INTO mk_pricing_rule (
|
|
pricing_parameter_id,
|
|
askili_yan,kategori,urun_ilk_grubu,urun_ana_grubu,urun_alt_grubu,
|
|
icerik,karisim,marka,brand_code,brand_group,
|
|
strategy_code,anchor_mode,calc_enabled,publish_postgres,publish_nebim,
|
|
is_active,created_at,updated_at
|
|
)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,now(),now())
|
|
RETURNING id
|
|
`, nullablePricingParameterID(item.PricingParameterID), pq.Array(item.AskiliYan), pq.Array(item.Kategori), pq.Array(item.UrunIlkGrubu), pq.Array(item.UrunAnaGrubu), pq.Array(item.UrunAltGrubu),
|
|
pq.Array(item.Icerik), pq.Array(item.Karisim), pq.Array(item.Marka), pq.Array(item.BrandCode), pq.Array(item.BrandGroupSec),
|
|
item.StrategyCode, item.AnchorMode, item.CalcEnabled, item.PublishPostgres, item.PublishNebim,
|
|
item.IsActive,
|
|
).Scan(&id); err != nil {
|
|
return "", err
|
|
}
|
|
} else {
|
|
if _, err := tx.ExecContext(ctx, `
|
|
UPDATE mk_pricing_rule SET
|
|
pricing_parameter_id=$2,
|
|
askili_yan=$3,
|
|
kategori=$4,
|
|
urun_ilk_grubu=$5,
|
|
urun_ana_grubu=$6,
|
|
urun_alt_grubu=$7,
|
|
icerik=$8,
|
|
karisim=$9,
|
|
marka=$10,
|
|
brand_code=$11,
|
|
brand_group=$12,
|
|
strategy_code=$13,
|
|
anchor_mode=$14,
|
|
calc_enabled=$15,
|
|
publish_postgres=$16,
|
|
publish_nebim=$17,
|
|
is_active=$18,
|
|
updated_at=now()
|
|
WHERE id=$1
|
|
`, id,
|
|
nullablePricingParameterID(item.PricingParameterID),
|
|
pq.Array(item.AskiliYan), pq.Array(item.Kategori), pq.Array(item.UrunIlkGrubu), pq.Array(item.UrunAnaGrubu), pq.Array(item.UrunAltGrubu),
|
|
pq.Array(item.Icerik), pq.Array(item.Karisim), pq.Array(item.Marka), pq.Array(item.BrandCode), pq.Array(item.BrandGroupSec),
|
|
item.StrategyCode, item.AnchorMode, item.CalcEnabled, item.PublishPostgres, item.PublishNebim,
|
|
item.IsActive,
|
|
); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
// multipliers upsert helper
|
|
upsertX := func(cur string, base, m1, m2, m3, m4, m5, m6 float64) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
INSERT INTO mk_pricex (rule_id, currency, base_mult, m1, m2, m3, m4, m5, m6, created_at, updated_at)
|
|
VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,now(),now())
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
base_mult=EXCLUDED.base_mult,
|
|
m1=EXCLUDED.m1,
|
|
m2=EXCLUDED.m2,
|
|
m3=EXCLUDED.m3,
|
|
m4=EXCLUDED.m4,
|
|
m5=EXCLUDED.m5,
|
|
m6=EXCLUDED.m6,
|
|
updated_at=now()
|
|
`, id, cur, base, m1, m2, m3, m4, m5, m6)
|
|
return err
|
|
}
|
|
upsertRoll := func(cur string, wholesaleStep, retailStep float64, retailMode string) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
INSERT INTO mk_priceroll (rule_id, currency, step, wholesale_step, retail_step, retail_mode, created_at, updated_at)
|
|
VALUES ($1,$2,$3,$4,$5,$6,now(),now())
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
step=EXCLUDED.step,
|
|
wholesale_step=EXCLUDED.wholesale_step,
|
|
retail_step=EXCLUDED.retail_step,
|
|
retail_mode=EXCLUDED.retail_mode,
|
|
updated_at=now()
|
|
`, id, cur, wholesaleStep, wholesaleStep, retailStep, retailMode)
|
|
return err
|
|
}
|
|
|
|
if err := upsertX("TRY", item.TryBase, item.Try1, item.Try2, item.Try3, item.Try4, item.Try5, item.Try6); err != nil {
|
|
return "", err
|
|
}
|
|
if err := upsertRoll("TRY", item.TryWholesaleStep, item.TryRetailStep, item.TryRetailMode); err != nil {
|
|
return "", err
|
|
}
|
|
if err := upsertX("USD", item.UsdBase, item.Usd1, item.Usd2, item.Usd3, item.Usd4, item.Usd5, item.Usd6); err != nil {
|
|
return "", err
|
|
}
|
|
if err := upsertRoll("USD", item.UsdWholesaleStep, item.UsdRetailStep, item.UsdRetailMode); err != nil {
|
|
return "", err
|
|
}
|
|
if err := upsertX("EUR", item.EurBase, item.Eur1, item.Eur2, item.Eur3, item.Eur4, item.Eur5, item.Eur6); err != nil {
|
|
return "", err
|
|
}
|
|
if err := upsertRoll("EUR", item.EurWholesaleStep, item.EurRetailStep, item.EurRetailMode); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return id, nil
|
|
}
|
|
|
|
// UpdatePricingRuleByIDFast updates an existing rule without parameter versioning/scope fill.
|
|
// This is the fast path for worksheet saves where rule_id is already known.
|
|
func UpdatePricingRuleByIDFast(ctx context.Context, tx *sql.Tx, item PricingRuleSaveItem) error {
|
|
if tx == nil {
|
|
return fmt.Errorf("nil tx")
|
|
}
|
|
ruleID := strings.TrimSpace(item.ID)
|
|
if ruleID == "" {
|
|
return fmt.Errorf("missing rule id")
|
|
}
|
|
|
|
item.TryRetailMode = normalizeRetailMode(item.TryRetailMode)
|
|
item.UsdRetailMode = normalizeRetailMode(item.UsdRetailMode)
|
|
item.EurRetailMode = normalizeRetailMode(item.EurRetailMode)
|
|
|
|
if _, err := tx.ExecContext(ctx, `
|
|
UPDATE mk_pricing_rule SET
|
|
calc_enabled=$2,
|
|
publish_postgres=$3,
|
|
publish_nebim=$4,
|
|
is_active=$5,
|
|
updated_at=now()
|
|
WHERE id=$1
|
|
`, ruleID, item.CalcEnabled, item.PublishPostgres, item.PublishNebim, item.IsActive); err != nil {
|
|
return err
|
|
}
|
|
|
|
upsertX := func(cur string, base, m1, m2, m3, m4, m5, m6 float64) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
INSERT INTO mk_pricex (rule_id, currency, base_mult, m1, m2, m3, m4, m5, m6, created_at, updated_at)
|
|
VALUES (NULLIF($1,'')::uuid,$2,$3,$4,$5,$6,$7,$8,$9,now(),now())
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
base_mult=EXCLUDED.base_mult,
|
|
m1=EXCLUDED.m1,
|
|
m2=EXCLUDED.m2,
|
|
m3=EXCLUDED.m3,
|
|
m4=EXCLUDED.m4,
|
|
m5=EXCLUDED.m5,
|
|
m6=EXCLUDED.m6,
|
|
updated_at=now()
|
|
`, ruleID, cur, base, m1, m2, m3, m4, m5, m6)
|
|
return err
|
|
}
|
|
|
|
upsertRoll := func(cur string, wholesaleStep, retailStep float64, retailMode string) error {
|
|
_, err := tx.ExecContext(ctx, `
|
|
INSERT INTO mk_priceroll (rule_id, currency, step, wholesale_step, retail_step, retail_mode, created_at, updated_at)
|
|
VALUES (NULLIF($1,'')::uuid,$2,$3,$4,$5,$6,now(),now())
|
|
ON CONFLICT (rule_id, currency) DO UPDATE SET
|
|
step=EXCLUDED.step,
|
|
wholesale_step=EXCLUDED.wholesale_step,
|
|
retail_step=EXCLUDED.retail_step,
|
|
retail_mode=EXCLUDED.retail_mode,
|
|
updated_at=now()
|
|
`, ruleID, cur, wholesaleStep, wholesaleStep, retailStep, retailMode)
|
|
return err
|
|
}
|
|
|
|
if err := upsertX("TRY", item.TryBase, item.Try1, item.Try2, item.Try3, item.Try4, item.Try5, item.Try6); err != nil {
|
|
return err
|
|
}
|
|
if err := upsertRoll("TRY", item.TryWholesaleStep, item.TryRetailStep, item.TryRetailMode); err != nil {
|
|
return err
|
|
}
|
|
if err := upsertX("USD", item.UsdBase, item.Usd1, item.Usd2, item.Usd3, item.Usd4, item.Usd5, item.Usd6); err != nil {
|
|
return err
|
|
}
|
|
if err := upsertRoll("USD", item.UsdWholesaleStep, item.UsdRetailStep, item.UsdRetailMode); err != nil {
|
|
return err
|
|
}
|
|
if err := upsertX("EUR", item.EurBase, item.Eur1, item.Eur2, item.Eur3, item.Eur4, item.Eur5, item.Eur6); err != nil {
|
|
return err
|
|
}
|
|
if err := upsertRoll("EUR", item.EurWholesaleStep, item.EurRetailStep, item.EurRetailMode); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UpsertPricingRuleByParameterIDFast ensures there is a rule row for a pricing_parameter_id and
|
|
// updates its multipliers/roundings in place. This avoids expensive parameter versioning and
|
|
// scope fill during worksheet-style bulk saves.
|
|
func UpsertPricingRuleByParameterIDFast(ctx context.Context, tx *sql.Tx, item PricingRuleSaveItem) (string, error) {
|
|
if tx == nil {
|
|
return "", fmt.Errorf("nil tx")
|
|
}
|
|
if item.PricingParameterID <= 0 {
|
|
return "", fmt.Errorf("missing pricing_parameter_id")
|
|
}
|
|
|
|
// Find latest rule for this parameter id (if any).
|
|
var ruleID string
|
|
_ = tx.QueryRowContext(ctx, `
|
|
SELECT id::text
|
|
FROM mk_pricing_rule
|
|
WHERE pricing_parameter_id = $1
|
|
ORDER BY created_at DESC, updated_at DESC, id DESC
|
|
LIMIT 1
|
|
FOR UPDATE
|
|
`, item.PricingParameterID).Scan(&ruleID)
|
|
ruleID = strings.TrimSpace(ruleID)
|
|
|
|
if ruleID == "" {
|
|
// Create minimal rule row; other fields have defaults and parameter scope is read from mk_urunpricingprmtr.
|
|
if err := tx.QueryRowContext(ctx, `
|
|
INSERT INTO mk_pricing_rule (
|
|
pricing_parameter_id,
|
|
calc_enabled,
|
|
publish_postgres,
|
|
publish_nebim,
|
|
is_active,
|
|
created_at,
|
|
updated_at
|
|
)
|
|
VALUES ($1,$2,$3,$4,$5,now(),now())
|
|
RETURNING id::text
|
|
`, item.PricingParameterID, item.CalcEnabled, item.PublishPostgres, item.PublishNebim, item.IsActive).Scan(&ruleID); err != nil {
|
|
return "", err
|
|
}
|
|
ruleID = strings.TrimSpace(ruleID)
|
|
}
|
|
if ruleID == "" {
|
|
return "", fmt.Errorf("failed to resolve rule id")
|
|
}
|
|
|
|
// Reuse the ID-fast updater now that we have an id.
|
|
item.ID = ruleID
|
|
if err := UpdatePricingRuleByIDFast(ctx, tx, item); err != nil {
|
|
return "", err
|
|
}
|
|
return ruleID, nil
|
|
}
|
|
|
|
func nullablePricingParameterID(id int64) any {
|
|
if id <= 0 {
|
|
return nil
|
|
}
|
|
return id
|
|
}
|
|
|
|
type PricingRuleOptionFilters struct {
|
|
AskiliYan []string
|
|
Kategori []string
|
|
UrunIlkGrubu []string
|
|
UrunAnaGrubu []string
|
|
UrunAltGrubu []string
|
|
Icerik []string
|
|
Marka []string
|
|
BrandCode []string
|
|
BrandGroupSec []string
|
|
}
|
|
|
|
// ListPricingRuleDistinctOptions returns distinct values for the requested field, applying cascade filters.
|
|
// Source is MSSQL ProductFilterWithDescription('TR').
|
|
func ListPricingRuleDistinctOptions(ctx context.Context, mssql *sql.DB, field string, f PricingRuleOptionFilters, limit int) ([]string, error) {
|
|
field = strings.TrimSpace(field)
|
|
if limit <= 0 {
|
|
limit = 500
|
|
}
|
|
|
|
// Map API field -> MSSQL expression (TR descriptions + raw codes where needed)
|
|
type fieldExpr struct {
|
|
Expr string
|
|
// For BrandGroupSec we need to compute from Postgres later; for now it comes from ProductPricing list,
|
|
// so we only expose BrandCode/Marka cascades from MSSQL.
|
|
}
|
|
fieldMap := map[string]string{
|
|
"askili_yan": "COALESCE(LTRIM(RTRIM(ProductAtt45Desc)), '')",
|
|
"kategori": "COALESCE(LTRIM(RTRIM(ProductAtt44Desc)), '')",
|
|
"urun_ilk_grubu": "COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '')",
|
|
"urun_ana_grubu": "COALESCE(LTRIM(RTRIM(ProductAtt01Desc)), '')",
|
|
"urun_alt_grubu": "COALESCE(LTRIM(RTRIM(ProductAtt02Desc)), '')",
|
|
"icerik": "COALESCE(LTRIM(RTRIM(ProductAtt41Desc)), '')",
|
|
"marka": "COALESCE(LTRIM(RTRIM(ProductAtt10Desc)), '')",
|
|
"brand_code": "COALESCE(LTRIM(RTRIM(ProductAtt10)), '')",
|
|
// "brand_group" is not MSSQL-backed (comes from mk_brandgrpmatch); handled later.
|
|
}
|
|
expr, ok := fieldMap[field]
|
|
if !ok {
|
|
return nil, fmt.Errorf("invalid field")
|
|
}
|
|
|
|
// Build WHERE with OR lists like other endpoints
|
|
paramIndex := 1
|
|
args := make([]any, 0, 64)
|
|
nextParam := func() string {
|
|
p := "@p" + strconv.Itoa(paramIndex)
|
|
paramIndex++
|
|
return p
|
|
}
|
|
whereParts := []string{
|
|
"ProductAtt42 IN ('SERI', 'AKSESUAR')",
|
|
"IsBlocked = 0",
|
|
"LEN(LTRIM(RTRIM(ProductCode))) = 13",
|
|
}
|
|
addIn := func(expr string, values []string) {
|
|
clean := make([]string, 0, len(values))
|
|
for _, v := range values {
|
|
v = strings.TrimSpace(v)
|
|
if v != "" {
|
|
clean = append(clean, v)
|
|
}
|
|
}
|
|
if len(clean) == 0 {
|
|
return
|
|
}
|
|
ors := make([]string, 0, len(clean))
|
|
for _, v := range clean {
|
|
p := nextParam()
|
|
ors = append(ors, expr+" = "+p)
|
|
args = append(args, v)
|
|
}
|
|
whereParts = append(whereParts, "("+strings.Join(ors, " OR ")+")")
|
|
}
|
|
|
|
addIn(fieldMap["askili_yan"], f.AskiliYan)
|
|
addIn(fieldMap["kategori"], f.Kategori)
|
|
addIn(fieldMap["urun_ilk_grubu"], f.UrunIlkGrubu)
|
|
addIn(fieldMap["urun_ana_grubu"], f.UrunAnaGrubu)
|
|
addIn(fieldMap["urun_alt_grubu"], f.UrunAltGrubu)
|
|
addIn(fieldMap["icerik"], f.Icerik)
|
|
addIn(fieldMap["marka"], f.Marka)
|
|
addIn(fieldMap["brand_code"], f.BrandCode)
|
|
|
|
whereSQL := strings.Join(whereParts, " AND ")
|
|
q := `
|
|
SELECT TOP (` + strconv.Itoa(limit) + `)
|
|
v
|
|
FROM (
|
|
SELECT DISTINCT ` + expr + ` AS v
|
|
FROM ProductFilterWithDescription('TR')
|
|
WHERE ` + whereSQL + `
|
|
) t
|
|
WHERE ISNULL(LTRIM(RTRIM(v)), '') <> ''
|
|
ORDER BY v;
|
|
`
|
|
rows, err := mssql.QueryContext(ctx, q, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
out := make([]string, 0, limit)
|
|
for rows.Next() {
|
|
var v string
|
|
if err := rows.Scan(&v); err != nil {
|
|
return nil, err
|
|
}
|
|
v = strings.TrimSpace(v)
|
|
if v != "" {
|
|
out = append(out, v)
|
|
}
|
|
}
|
|
return out, rows.Err()
|
|
}
|