Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-23 17:45:15 +03:00
parent c2e2c6a295
commit 0f8a456ea0

View File

@@ -14,6 +14,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@@ -77,6 +78,90 @@ type productSeriesAutoStats struct {
Skipped int
}
var productSeriesFallbackMu sync.Mutex
var productSeriesFallbackCachedCode string
var productSeriesFallbackCachedID int64
var productSeriesFallbackCachedAt time.Time
func productSeriesFallbackEnabled() bool {
// Default on: user explicitly requested "stok var ama seri yoksa 1- ata".
raw := strings.TrimSpace(strings.ToLower(os.Getenv("PRODUCT_SERIES_FALLBACK_ON_EMPTY")))
if raw == "" {
return true
}
return raw == "1" || raw == "true" || raw == "on" || raw == "yes"
}
func productSeriesFallbackCode() string {
code := strings.TrimSpace(os.Getenv("PRODUCT_SERIES_FALLBACK_SERIES_CODE"))
if code == "" {
code = "1-"
}
return code
}
func productSeriesResolveFallbackSeries(ctx context.Context, pg *sql.DB) (int64, string, error) {
if !productSeriesFallbackEnabled() {
return 0, "", nil
}
code := productSeriesFallbackCode()
if code == "" {
return 0, "", nil
}
productSeriesFallbackMu.Lock()
// Cache for a bit to avoid repeated lookups under high throughput.
if productSeriesFallbackCachedCode == code && productSeriesFallbackCachedAt.After(time.Now().Add(-10*time.Minute)) {
id := productSeriesFallbackCachedID
productSeriesFallbackMu.Unlock()
return id, code, nil
}
productSeriesFallbackMu.Unlock()
var id int64
var gotCode string
err := pg.QueryRowContext(ctx, `
SELECT id, COALESCE(code,'')
FROM dfgrp
WHERE master='zbggseri'
AND COALESCE(is_active, TRUE)=TRUE
AND (
code = $1
OR title = $1
OR title LIKE $1 || '%'
)
ORDER BY
CASE
WHEN code = $1 THEN 0
WHEN title = $1 THEN 1
ELSE 2
END,
id
LIMIT 1
`, code).Scan(&id, &gotCode)
if err != nil {
if err == sql.ErrNoRows {
// cache negative for a short period too
productSeriesFallbackMu.Lock()
productSeriesFallbackCachedCode = code
productSeriesFallbackCachedID = 0
productSeriesFallbackCachedAt = time.Now()
productSeriesFallbackMu.Unlock()
return 0, code, nil
}
return 0, code, err
}
if gotCode != "" {
code = gotCode
}
productSeriesFallbackMu.Lock()
productSeriesFallbackCachedCode = code
productSeriesFallbackCachedID = id
productSeriesFallbackCachedAt = time.Now()
productSeriesFallbackMu.Unlock()
return id, code, nil
}
var productSeriesSizeGroups = map[string][]string{
"tak": {"44", "46", "48", "50", "52", "54", "56", "58", "60", "62", "64", "66", "68", "70", "72", "74"},
"ayk": {"39", "40", "41", "42", "43", "44", "45", "46"},
@@ -345,6 +430,35 @@ func productSeriesApplyVariant(ctx context.Context, pg *sql.DB, v productSeriesA
return 0, 1, nil
}
selected := productSeriesSelectRules(v, rules)
if len(selected) == 0 {
fallbackID, _, err := productSeriesResolveFallbackSeries(ctx, pg)
if err != nil {
return 0, 1, err
}
if fallbackID > 0 {
// Only apply fallback when the variant has no assignments yet.
var exists int
checkErr := pg.QueryRowContext(ctx, `
SELECT 1
FROM zbggseri
WHERE mmitem_id=$1
AND dim1=$2
AND (($3::bigint IS NULL AND dim3 IS NULL) OR dim3=$3::bigint)
LIMIT 1
`, mmitemID, dim1ID, nullableInt64ForAuto(dim3ID)).Scan(&exists)
if checkErr == nil {
// keep existing manual/previous assignment; nothing to do
return 0, 0, nil
}
if checkErr != nil && checkErr != sql.ErrNoRows {
return 0, 1, checkErr
}
// Use the fallback series as the single selected rule.
selected = []productSeriesAutoRule{{SeriesID: fallbackID}}
} else {
return 0, 1, nil
}
}
if !apply {
return len(selected), 0, nil
}