package queries import ( "context" "database/sql" "fmt" "strings" "github.com/lib/pq" ) type BrandRow struct { BrandCode string `json:"brand_code"` BrandName string `json:"brand_name"` GroupID int `json:"group_id"` GroupCode string `json:"group_code"` GroupName string `json:"group_name"` } type BrandGroupOption struct { ID int `json:"id"` Code string `json:"code"` Title string `json:"title"` Description string `json:"description"` } func EnsureBrandClassificationTables(pg *sql.DB) error { stmts := []string{ ` CREATE TABLE IF NOT EXISTS mk_brands ( brand_code TEXT PRIMARY KEY, brand_name TEXT NOT NULL DEFAULT '', is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() )`, `CREATE INDEX IF NOT EXISTS ix_mk_brands_name ON mk_brands (brand_name)`, ` CREATE TABLE IF NOT EXISTS mk_brandgrp ( id SMALLINT PRIMARY KEY, code TEXT NOT NULL UNIQUE, title TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', sort_order SMALLINT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT now() )`, `ALTER TABLE mk_brandgrp ADD COLUMN IF NOT EXISTS description TEXT NOT NULL DEFAULT ''`, ` INSERT INTO mk_brandgrp (id, code, title, description, sort_order) VALUES (1, 'SARTORIAL', 'SARTORIAL', 'Klasik / terzilik odakli ana marka grubu', 1), (2, 'PREMIUM', 'PREMIUM', 'Ust segment / premium koleksiyon marka grubu', 2), (3, 'CORE', 'CORE', 'Ana koleksiyon / temel marka grubu', 3) ON CONFLICT (id) DO NOTHING`, `UPDATE mk_brandgrp SET description='Klasik / terzilik odakli ana marka grubu' WHERE id=1 AND COALESCE(description,'')=''`, `UPDATE mk_brandgrp SET description='Ust segment / premium koleksiyon marka grubu' WHERE id=2 AND COALESCE(description,'')=''`, `UPDATE mk_brandgrp SET description='Ana koleksiyon / temel marka grubu' WHERE id=3 AND COALESCE(description,'')=''`, ` CREATE TABLE IF NOT EXISTS mk_brandgrpmatch ( brand_code TEXT NOT NULL REFERENCES mk_brands(brand_code) ON DELETE CASCADE, grp_id SMALLINT NOT NULL REFERENCES mk_brandgrp(id) ON DELETE RESTRICT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (brand_code) )`, `CREATE INDEX IF NOT EXISTS ix_mk_brandgrpmatch_grp ON mk_brandgrpmatch (grp_id)`, } for _, s := range stmts { if _, err := pg.Exec(s); err != nil { return err } } return nil } func ListBrandGroups(ctx context.Context, pg *sql.DB) ([]BrandGroupOption, error) { rows, err := pg.QueryContext(ctx, `SELECT id, code, title, description FROM mk_brandgrp ORDER BY sort_order, id`) if err != nil { return nil, err } defer rows.Close() out := make([]BrandGroupOption, 0, 8) for rows.Next() { var o BrandGroupOption if err := rows.Scan(&o.ID, &o.Code, &o.Title, &o.Description); err != nil { return nil, err } o.Code = strings.TrimSpace(o.Code) o.Title = strings.TrimSpace(o.Title) o.Description = strings.TrimSpace(o.Description) out = append(out, o) } return out, rows.Err() } func ListBrandsWithGroups(ctx context.Context, pg *sql.DB, q string, limit int) ([]BrandRow, error) { if limit <= 0 { limit = 5000 } q = strings.TrimSpace(q) args := []any{} where := "" if q != "" { args = append(args, "%"+q+"%") where = "WHERE (b.brand_code ILIKE $1 OR b.brand_name ILIKE $1)" } args = append(args, limit) limitParam := fmt.Sprintf("$%d", len(args)) sqlq := ` SELECT b.brand_code, b.brand_name, COALESCE(m.grp_id, 0) AS group_id, COALESCE(g.code, '') AS group_code, COALESCE(g.title, '') AS group_name FROM mk_brands b LEFT JOIN mk_brandgrpmatch m ON m.brand_code = b.brand_code LEFT JOIN mk_brandgrp g ON g.id = m.grp_id ` + where + ` ORDER BY b.brand_code LIMIT ` + limitParam + ` ` rows, err := pg.QueryContext(ctx, sqlq, args...) if err != nil { return nil, err } defer rows.Close() out := make([]BrandRow, 0, 1024) for rows.Next() { var r BrandRow if err := rows.Scan(&r.BrandCode, &r.BrandName, &r.GroupID, &r.GroupCode, &r.GroupName); err != nil { return nil, err } r.BrandCode = strings.TrimSpace(r.BrandCode) r.BrandName = strings.TrimSpace(r.BrandName) r.GroupCode = strings.TrimSpace(r.GroupCode) r.GroupName = strings.TrimSpace(r.GroupName) out = append(out, r) } return out, rows.Err() } func UpsertBrand(ctx context.Context, tx *sql.Tx, code string, name string, active bool) error { code = strings.TrimSpace(code) name = strings.TrimSpace(name) if code == "" { return nil } _, err := tx.ExecContext(ctx, ` INSERT INTO mk_brands (brand_code, brand_name, is_active, created_at, updated_at) VALUES ($1, $2, $3, now(), now()) ON CONFLICT (brand_code) DO UPDATE SET brand_name = EXCLUDED.brand_name, is_active = EXCLUDED.is_active, updated_at = now() `, code, name, active) return err } func DeleteBrandsNotIn(ctx context.Context, tx *sql.Tx, keepCodes []string) error { // If keepCodes is empty, do nothing (avoid wiping table by mistake). if len(keepCodes) == 0 { return nil } // Use temp table style deletion via UNNEST. _, err := tx.ExecContext(ctx, ` DELETE FROM mk_brands WHERE brand_code NOT IN (SELECT UNNEST($1::text[])) `, pq.Array(keepCodes)) return err } func SetBrandGroup(ctx context.Context, tx *sql.Tx, brandCode string, grpID int) error { brandCode = strings.TrimSpace(brandCode) if brandCode == "" { return nil } if grpID <= 0 { _, err := tx.ExecContext(ctx, `DELETE FROM mk_brandgrpmatch WHERE brand_code=$1`, brandCode) return err } _, err := tx.ExecContext(ctx, ` INSERT INTO mk_brandgrpmatch (brand_code, grp_id, created_at, updated_at) VALUES ($1, $2, now(), now()) ON CONFLICT (brand_code) DO UPDATE SET grp_id = EXCLUDED.grp_id, updated_at = now() `, brandCode, grpID) return err }