238 lines
7.2 KiB
Go
238 lines
7.2 KiB
Go
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"`
|
|
AnchorMode string `json:"anchor_mode"`
|
|
}
|
|
|
|
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 '',
|
|
anchor_mode TEXT NOT NULL DEFAULT 'USD',
|
|
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 ''`,
|
|
`ALTER TABLE mk_brandgrp ADD COLUMN IF NOT EXISTS anchor_mode TEXT NOT NULL DEFAULT 'USD'`,
|
|
`UPDATE mk_brandgrp SET anchor_mode='USD' WHERE COALESCE(NULLIF(BTRIM(anchor_mode), ''), '') = ''`,
|
|
`ALTER TABLE mk_brandgrp DROP CONSTRAINT IF EXISTS ck_mk_brandgrp_anchor_mode`,
|
|
`ALTER TABLE mk_brandgrp ADD CONSTRAINT ck_mk_brandgrp_anchor_mode CHECK (anchor_mode IN ('TRY','USD'))`,
|
|
`
|
|
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, anchor_mode 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, &o.AnchorMode); err != nil {
|
|
return nil, err
|
|
}
|
|
o.Code = strings.TrimSpace(o.Code)
|
|
o.Title = strings.TrimSpace(o.Title)
|
|
o.Description = strings.TrimSpace(o.Description)
|
|
o.AnchorMode = strings.ToUpper(strings.TrimSpace(o.AnchorMode))
|
|
if o.AnchorMode == "" {
|
|
o.AnchorMode = "USD"
|
|
}
|
|
out = append(out, o)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func SetBrandGroupAnchorMode(ctx context.Context, tx *sql.Tx, grpID int, anchorMode string) error {
|
|
anchorMode = strings.ToUpper(strings.TrimSpace(anchorMode))
|
|
if anchorMode == "" {
|
|
anchorMode = "USD"
|
|
}
|
|
_, err := tx.ExecContext(ctx, `
|
|
UPDATE mk_brandgrp
|
|
SET anchor_mode=$2
|
|
WHERE id=$1
|
|
`, grpID, anchorMode)
|
|
return err
|
|
}
|
|
|
|
func SyncPricingRuleAnchorModesByGroup(ctx context.Context, tx *sql.Tx, grpID int, anchorMode string) error {
|
|
anchorMode = strings.ToUpper(strings.TrimSpace(anchorMode))
|
|
if anchorMode == "" {
|
|
anchorMode = "USD"
|
|
}
|
|
_, err := tx.ExecContext(ctx, `
|
|
UPDATE mk_pricing_rule r
|
|
SET anchor_mode=$2,
|
|
updated_at=now()
|
|
WHERE EXISTS (
|
|
SELECT 1
|
|
FROM mk_brandgrp g
|
|
JOIN LATERAL unnest(r.brand_group) bg(value) ON TRUE
|
|
WHERE g.id=$1
|
|
AND (
|
|
UPPER(BTRIM(bg.value)) = UPPER(BTRIM(g.code))
|
|
OR UPPER(BTRIM(bg.value)) = UPPER(BTRIM(g.title))
|
|
)
|
|
)
|
|
`, grpID, anchorMode)
|
|
return 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
|
|
}
|