Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-05-22 14:57:34 +03:00
parent d886fba6de
commit 1f90b9f9ce
25 changed files with 2767 additions and 687 deletions

View File

@@ -8,7 +8,6 @@ import (
"database/sql"
"encoding/json"
"net/http"
"sort"
"strings"
"github.com/gorilla/mux"
@@ -23,6 +22,42 @@ type FirstGroupMailLookupResponse struct {
Mails []models.MailOption `json:"mails"`
}
func ensureFirstGroupMailMappingTables(pg *sql.DB) error {
// Idempotent bootstrap: create tables if they don't exist.
// We keep schema minimal: (group_code, mail_id) + created_at and FK to mk_mail.
stmts := []string{
`
CREATE TABLE IF NOT EXISTS mk_costing_first_group_mail (
urun_ilk_grubu TEXT NOT NULL,
mail_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (urun_ilk_grubu, mail_id),
CONSTRAINT fk_costing_first_group_mail_mail
FOREIGN KEY (mail_id) REFERENCES mk_mail(id) ON DELETE CASCADE
)
`,
`CREATE INDEX IF NOT EXISTS ix_costing_first_group_mail_group ON mk_costing_first_group_mail (urun_ilk_grubu)`,
`
CREATE TABLE IF NOT EXISTS mk_pricing_first_group_mail (
urun_ilk_grubu TEXT NOT NULL,
mail_id UUID NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
PRIMARY KEY (urun_ilk_grubu, mail_id),
CONSTRAINT fk_pricing_first_group_mail_mail
FOREIGN KEY (mail_id) REFERENCES mk_mail(id) ON DELETE CASCADE
)
`,
`CREATE INDEX IF NOT EXISTS ix_pricing_first_group_mail_group ON mk_pricing_first_group_mail (urun_ilk_grubu)`,
}
for _, s := range stmts {
if _, err := pg.Exec(s); err != nil {
return err
}
}
return nil
}
func GetCostingFirstGroupMailMappingLookupsHandler(pg *sql.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
@@ -32,6 +67,10 @@ func GetCostingFirstGroupMailMappingLookupsHandler(pg *sql.DB) http.HandlerFunc
http.Error(w, "mssql connection not available", http.StatusServiceUnavailable)
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
return
}
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
@@ -39,21 +78,23 @@ func GetCostingFirstGroupMailMappingLookupsHandler(pg *sql.DB) http.HandlerFunc
firstGroups := make([]models.FirstGroupOption, 0, 256)
mails := make([]models.MailOption, 0, 256)
fgRows, err := queries.ListProductFirstGroupOptions(ctx, mssql, "", 5000)
fgRows, err := queries.ListProductFirstGroupCodeDescOptions(ctx, mssql, "", 5000)
if err != nil {
http.Error(w, "first group lookup error", http.StatusInternalServerError)
return
}
defer fgRows.Close()
for fgRows.Next() {
var g string
if err := fgRows.Scan(&g); err != nil {
var code string
var title string
if err := fgRows.Scan(&code, &title); err != nil {
http.Error(w, "first group scan error", http.StatusInternalServerError)
return
}
g = strings.TrimSpace(g)
if g != "" {
firstGroups = append(firstGroups, models.FirstGroupOption{ID: g, Label: g})
code = strings.TrimSpace(code)
title = strings.TrimSpace(title)
if code != "" {
firstGroups = append(firstGroups, models.FirstGroupOption{Code: code, Title: title})
}
}
if err := fgRows.Err(); err != nil {
@@ -98,34 +139,44 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
http.Error(w, "mssql connection not available", http.StatusServiceUnavailable)
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
return
}
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
// Fetch all first groups from V3 (source of truth for the list)
allGroups := make([]string, 0, 512)
fgRows, err := queries.ListProductFirstGroupOptions(ctx, mssql, "", 5000)
allCodes := make([]string, 0, 512)
titleByCode := make(map[string]string, 512)
fgRows, err := queries.ListProductFirstGroupCodeDescOptions(ctx, mssql, "", 5000)
if err != nil {
http.Error(w, "first group lookup error", http.StatusInternalServerError)
return
}
defer fgRows.Close()
for fgRows.Next() {
var g string
if err := fgRows.Scan(&g); err != nil {
var code string
var title string
if err := fgRows.Scan(&code, &title); err != nil {
http.Error(w, "first group scan error", http.StatusInternalServerError)
return
}
g = strings.TrimSpace(g)
if g != "" {
allGroups = append(allGroups, g)
code = strings.TrimSpace(code)
title = strings.TrimSpace(title)
if code != "" {
allCodes = append(allCodes, code)
if _, ok := titleByCode[code]; !ok {
titleByCode[code] = title
}
}
}
if err := fgRows.Err(); err != nil {
http.Error(w, "first group rows error", http.StatusInternalServerError)
return
}
sort.Strings(allGroups)
allCodes = normalizeIDList(allCodes)
// Fetch mappings from Postgres
rows, err := pg.Query(queries.GetCostingFirstGroupMailMappingRows)
@@ -136,9 +187,11 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
defer rows.Close()
byGroup := map[string]*models.FirstGroupMailMappingRow{}
for _, g := range allGroups {
byGroup[g] = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: g,
for _, code := range allCodes {
byGroup[code] = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: code,
GroupCode: code,
GroupTitle: titleByCode[code],
MailIDs: make([]string, 0, 8),
Mails: make([]models.FirstGroupMailOption, 0, 8),
}
@@ -153,19 +206,21 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
http.Error(w, "mapping scan error", http.StatusInternalServerError)
return
}
g := strings.TrimSpace(group.String)
if g == "" {
code := strings.TrimSpace(group.String)
if code == "" {
continue
}
row, ok := byGroup[g]
row, ok := byGroup[code]
if !ok {
row = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: g,
UrunIlkGrubu: code,
GroupCode: code,
GroupTitle: titleByCode[code],
MailIDs: make([]string, 0, 8),
Mails: make([]models.FirstGroupMailOption, 0, 8),
}
byGroup[g] = row
allGroups = append(allGroups, g)
byGroup[code] = row
allCodes = append(allCodes, code)
}
if mailID.Valid && strings.TrimSpace(mailID.String) != "" {
id := strings.TrimSpace(mailID.String)
@@ -182,11 +237,15 @@ func GetCostingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
return
}
sort.Strings(allGroups)
out := make([]models.FirstGroupMailMappingRow, 0, len(allGroups))
for _, g := range allGroups {
if r := byGroup[g]; r != nil {
allCodes = normalizeIDList(allCodes)
out := make([]models.FirstGroupMailMappingRow, 0, len(allCodes))
for _, code := range allCodes {
if r := byGroup[code]; r != nil {
r.MailIDs = normalizeIDList(r.MailIDs)
// Fill title if missing
if strings.TrimSpace(r.GroupTitle) == "" {
r.GroupTitle = titleByCode[code]
}
out = append(out, *r)
}
}
@@ -203,33 +262,43 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
http.Error(w, "mssql connection not available", http.StatusServiceUnavailable)
return
}
if err := ensureFirstGroupMailMappingTables(pg); err != nil {
http.Error(w, "mapping table bootstrap error", http.StatusInternalServerError)
return
}
traceID := utils.TraceIDFromRequest(r)
ctx := utils.ContextWithTraceID(r.Context(), traceID)
allGroups := make([]string, 0, 512)
fgRows, err := queries.ListProductFirstGroupOptions(ctx, mssql, "", 5000)
allCodes := make([]string, 0, 512)
titleByCode := make(map[string]string, 512)
fgRows, err := queries.ListProductFirstGroupCodeDescOptions(ctx, mssql, "", 5000)
if err != nil {
http.Error(w, "first group lookup error", http.StatusInternalServerError)
return
}
defer fgRows.Close()
for fgRows.Next() {
var g string
if err := fgRows.Scan(&g); err != nil {
var code string
var title string
if err := fgRows.Scan(&code, &title); err != nil {
http.Error(w, "first group scan error", http.StatusInternalServerError)
return
}
g = strings.TrimSpace(g)
if g != "" {
allGroups = append(allGroups, g)
code = strings.TrimSpace(code)
title = strings.TrimSpace(title)
if code != "" {
allCodes = append(allCodes, code)
if _, ok := titleByCode[code]; !ok {
titleByCode[code] = title
}
}
}
if err := fgRows.Err(); err != nil {
http.Error(w, "first group rows error", http.StatusInternalServerError)
return
}
sort.Strings(allGroups)
allCodes = normalizeIDList(allCodes)
rows, err := pg.Query(queries.GetPricingFirstGroupMailMappingRows)
if err != nil {
@@ -239,9 +308,11 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
defer rows.Close()
byGroup := map[string]*models.FirstGroupMailMappingRow{}
for _, g := range allGroups {
byGroup[g] = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: g,
for _, code := range allCodes {
byGroup[code] = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: code,
GroupCode: code,
GroupTitle: titleByCode[code],
MailIDs: make([]string, 0, 8),
Mails: make([]models.FirstGroupMailOption, 0, 8),
}
@@ -256,19 +327,21 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
http.Error(w, "mapping scan error", http.StatusInternalServerError)
return
}
g := strings.TrimSpace(group.String)
if g == "" {
code := strings.TrimSpace(group.String)
if code == "" {
continue
}
row, ok := byGroup[g]
row, ok := byGroup[code]
if !ok {
row = &models.FirstGroupMailMappingRow{
UrunIlkGrubu: g,
UrunIlkGrubu: code,
GroupCode: code,
GroupTitle: titleByCode[code],
MailIDs: make([]string, 0, 8),
Mails: make([]models.FirstGroupMailOption, 0, 8),
}
byGroup[g] = row
allGroups = append(allGroups, g)
byGroup[code] = row
allCodes = append(allCodes, code)
}
if mailID.Valid && strings.TrimSpace(mailID.String) != "" {
id := strings.TrimSpace(mailID.String)
@@ -285,11 +358,14 @@ func GetPricingFirstGroupMailMappingsHandler(pg *sql.DB) http.HandlerFunc {
return
}
sort.Strings(allGroups)
out := make([]models.FirstGroupMailMappingRow, 0, len(allGroups))
for _, g := range allGroups {
if r := byGroup[g]; r != nil {
allCodes = normalizeIDList(allCodes)
out := make([]models.FirstGroupMailMappingRow, 0, len(allCodes))
for _, code := range allCodes {
if r := byGroup[code]; r != nil {
r.MailIDs = normalizeIDList(r.MailIDs)
if strings.TrimSpace(r.GroupTitle) == "" {
r.GroupTitle = titleByCode[code]
}
out = append(out, *r)
}
}