Files
bssapp/svc/routes/pricing_rules.go
2026-06-02 16:15:07 +03:00

164 lines
4.8 KiB
Go

package routes
import (
"bssapp-backend/queries"
"bssapp-backend/utils"
"database/sql"
"encoding/json"
"net/http"
"strconv"
"strings"
)
// Step-1/2 scope (distinct+cascade) comes from the PostgreSQL parameter cache.
// For now we implement:
// - Postgres tables (bootstrap)
// - List/Save rules (bulk)
// - Options endpoint for cascade (mk_urunpricingprmtr)
type PricingRuleBulkSavePayload struct {
Items []queries.PricingRuleSaveItem `json:"items"`
}
func GetPricingRulesHandler(pg *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
rows, err := queries.ListPricingRules(ctx, pg)
if err != nil {
http.Error(w, "pricing rules list error", http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(rows)
}
}
// Very small “bulk upsert” for step-1/2: we only need to persist the multipliers+roundings for now.
// Rules are identified by UUID; new rows can be created via empty id (server generates).
func SavePricingRulesBulkHandler(pg *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var payload PricingRuleBulkSavePayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, "invalid payload", http.StatusBadRequest)
return
}
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
tx, err := pg.BeginTx(ctx, nil)
if err != nil {
http.Error(w, "pg transaction start error", http.StatusInternalServerError)
return
}
defer tx.Rollback()
updated := 0
for _, it := range payload.Items {
// Zero means that no rounding rule has been configured yet.
if it.TryStep < 0 || it.UsdStep < 0 || it.EurStep < 0 {
http.Error(w, "invalid rounding step", http.StatusBadRequest)
return
}
id, err := queries.UpsertPricingRule(ctx, tx, it)
if err != nil {
http.Error(w, "pricing rule save error", http.StatusInternalServerError)
return
}
if id != "" {
updated++
}
}
if err := tx.Commit(); err != nil {
http.Error(w, "pg transaction commit error", http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{"success": true, "updated": updated})
}
}
func GetPricingRuleOptionsHandler(pg *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
field := strings.TrimSpace(r.URL.Query().Get("field"))
if field == "" {
http.Error(w, "missing field", http.StatusBadRequest)
return
}
limit := 500
if raw := strings.TrimSpace(r.URL.Query().Get("limit")); raw != "" {
if n, err := strconv.Atoi(raw); err == nil && n > 0 && n <= 5000 {
limit = n
}
}
f := pricingRuleFiltersFromRequest(r)
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
opts, err := queries.ListPricingParameterDistinctOptions(ctx, pg, field, f, limit)
if err != nil {
http.Error(w, "options lookup error", http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(map[string]any{
"field": field,
"options": opts,
})
}
}
func GetPricingParameterRulesHandler(pg *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
rows, err := queries.ListPricingParameterRules(ctx, pg, pricingRuleFiltersFromRequest(r))
if err != nil {
http.Error(w, "pricing parameter rules list error", http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(rows)
}
}
func pricingRuleFiltersFromRequest(r *http.Request) queries.PricingRuleOptionFilters {
return queries.PricingRuleOptionFilters{
AskiliYan: splitCSV(r.URL.Query().Get("askili_yan")),
Kategori: splitCSV(r.URL.Query().Get("kategori")),
UrunIlkGrubu: splitCSV(r.URL.Query().Get("urun_ilk_grubu")),
UrunAnaGrubu: splitCSV(r.URL.Query().Get("urun_ana_grubu")),
UrunAltGrubu: splitCSV(r.URL.Query().Get("urun_alt_grubu")),
Icerik: splitCSV(r.URL.Query().Get("icerik")),
Marka: splitCSV(r.URL.Query().Get("marka")),
BrandCode: splitCSV(r.URL.Query().Get("brand_code")),
BrandGroupSec: splitCSV(r.URL.Query().Get("brand_group")),
}
}
func splitCSV(raw string) []string {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
parts := strings.Split(raw, ",")
out := make([]string, 0, len(parts))
for _, p := range parts {
p = strings.TrimSpace(p)
if p != "" {
out = append(out, p)
}
}
return out
}