Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-24 20:35:55 +03:00
parent 10fd77bece
commit ec21dfac70
2 changed files with 58 additions and 10 deletions

View File

@@ -709,6 +709,11 @@ func productSeriesApplyVariant(ctx context.Context, pg *sql.DB, v productSeriesA
return 0, 0, err
}
defer tx.Rollback()
// Tag the DB session for audit attribution (scheduler vs http handler).
// This affects only statements in this transaction.
_, _ = tx.ExecContext(ctx, `SELECT set_config('application_name', current_setting('application_name') || '|series-auto', true)`)
if _, err := tx.ExecContext(ctx, `
DELETE FROM zbggseri
WHERE mmitem_id=$1

View File

@@ -449,12 +449,30 @@ func PostProductSeriesMappingsSaveHandler(pg *sql.DB) http.HandlerFunc {
}
defer tx.Rollback()
// Tag the DB session for audit attribution (http handler vs scheduler).
// This affects only statements in this transaction.
_, _ = tx.ExecContext(ctx, `SELECT set_config('application_name', current_setting('application_name') || '|series-save', true)`)
// Safety defaults:
// - Don't allow accidental clearing when UI sends empty series_ids.
// - Don't allow saving fallback code=1 from UI unless explicitly enabled.
allowClear := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("clear"))) == "true"
allowFallback := strings.TrimSpace(strings.ToLower(r.URL.Query().Get("allow_fallback"))) == "true"
log.Printf("[ProductSeriesMappingsSave] items=%d allow_clear=%t allow_fallback=%t remote=%s ua=%s",
len(req.Items), allowClear, allowFallback, strings.TrimSpace(r.RemoteAddr), strings.TrimSpace(r.UserAgent()))
var fallbackSeriesID int64 = 0
_ = tx.QueryRowContext(ctx, `SELECT id FROM dfgrp WHERE master='zbggseri' AND code='1'`).Scan(&fallbackSeriesID)
saved := 0
skipped := 0
for _, item := range req.Items {
code := strings.TrimSpace(item.ProductCode)
color := strings.TrimSpace(item.ColorCode)
dim3Token := strings.TrimSpace(item.Dim3Code)
if code == "" || color == "" {
skipped++
continue
}
mmitemID, err := resolveMmitemIDTx(ctx, tx, code)
@@ -477,6 +495,39 @@ func PostProductSeriesMappingsSaveHandler(pg *sql.DB) http.HandlerFunc {
dim3ID = sql.NullInt64{Int64: id, Valid: true}
}
// Normalize/validate series ids.
seen := map[int64]struct{}{}
seriesIDs := make([]int64, 0, len(item.SeriesIDs))
for _, seriesID := range item.SeriesIDs {
if seriesID <= 0 {
continue
}
if fallbackSeriesID > 0 && seriesID == fallbackSeriesID && !allowFallback {
continue
}
if _, ok := seen[seriesID]; ok {
continue
}
seen[seriesID] = struct{}{}
seriesIDs = append(seriesIDs, seriesID)
}
// Prevent accidental destructive writes from UI.
if len(seriesIDs) == 0 && !allowClear {
log.Printf("[ProductSeriesMappingsSave] skip_empty_series_ids product=%s color=%s dim3=%s", code, color, dim3Token)
skipped++
continue
}
{
out := make([]string, 0, len(seriesIDs))
for _, id := range seriesIDs {
out = append(out, fmt.Sprintf("%d", id))
}
log.Printf("[ProductSeriesMappingsSave] write product=%s color=%s dim3=%s series_ids=%s",
code, color, dim3Token, strings.Join(out, ","))
}
if _, err := tx.ExecContext(ctx, `
DELETE FROM zbggseri
WHERE mmitem_id=$1
@@ -486,15 +537,7 @@ WHERE mmitem_id=$1
http.Error(w, "Seri eslesmesi temizlenemedi: "+err.Error(), http.StatusInternalServerError)
return
}
seen := map[int64]struct{}{}
for _, seriesID := range item.SeriesIDs {
if seriesID <= 0 {
continue
}
if _, ok := seen[seriesID]; ok {
continue
}
seen[seriesID] = struct{}{}
for _, seriesID := range seriesIDs {
if _, err := tx.ExecContext(ctx, `
INSERT INTO zbggseri (mmitem_id, dim1, seri_id, dim3)
VALUES ($1, $2, $3, $4)
@@ -510,7 +553,7 @@ VALUES ($1, $2, $3, $4)
http.Error(w, "Seri eslesmeleri kaydedilemedi: "+err.Error(), http.StatusInternalServerError)
return
}
writeJSON(w, map[string]any{"saved": saved})
writeJSON(w, map[string]any{"saved": saved, "skipped": skipped})
}
}