Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-24 11:25:11 +03:00
parent 8866361a65
commit 01e457630c

View File

@@ -793,6 +793,11 @@ ORDER BY d.id, r.priority DESC, r.id
} }
func productSeriesSelectRules(v productSeriesAutoVariant, rules []productSeriesAutoRule) []productSeriesAutoRule { func productSeriesSelectRules(v productSeriesAutoVariant, rules []productSeriesAutoRule) []productSeriesAutoRule {
selectionMode := strings.TrimSpace(strings.ToLower(os.Getenv("PRODUCT_SERIES_SELECTION_MODE")))
if selectionMode == "" {
selectionMode = "dfs"
}
candidates := make([]productSeriesAutoRule, 0, len(rules)) candidates := make([]productSeriesAutoRule, 0, len(rules))
for _, rule := range rules { for _, rule := range rules {
if !productSeriesRuleFitsGroup(v.GroupKey, rule) { if !productSeriesRuleFitsGroup(v.GroupKey, rule) {
@@ -805,6 +810,58 @@ func productSeriesSelectRules(v productSeriesAutoVariant, rules []productSeriesA
if len(candidates) == 0 { if len(candidates) == 0 {
return nil return nil
} }
if selectionMode == "greedy" {
// Greedy: start from the widest series (most sizes), then narrower, until nothing fits.
// This matches the operational expectation: cover as much of the stock-size space as possible
// using broad series first, then progressively narrower series; fallback=1 only if nothing matches.
sort.Slice(candidates, func(i, j int) bool {
li := len(candidates[i].Ratio)
lj := len(candidates[j].Ratio)
if li != lj {
return li > lj
}
if candidates[i].Priority != candidates[j].Priority {
return candidates[i].Priority > candidates[j].Priority
}
// Prefer numeric codes ascending when comparable, otherwise lexicographic.
ai, ei := strconv.Atoi(strings.TrimSpace(candidates[i].Code))
aj, ej := strconv.Atoi(strings.TrimSpace(candidates[j].Code))
if ei == nil && ej == nil && ai != aj {
return ai < aj
}
return candidates[i].Code < candidates[j].Code
})
stock := copyProductSeriesStock(v.SizeQty)
picked := make([]productSeriesAutoRule, 0, 8)
used := make([]bool, len(candidates))
for {
pickedOne := false
for i := range candidates {
if used[i] {
continue
}
rule := candidates[i]
if productSeriesCanConsume(stock, rule) {
used[i] = true
picked = append(picked, rule)
productSeriesConsume(stock, rule, -1)
pickedOne = true
break // restart from widest again
}
}
if !pickedOne {
break
}
}
if len(picked) == 0 {
return nil
}
sort.Slice(picked, func(i, j int) bool { return picked[i].Code < picked[j].Code })
return picked
}
if len(candidates) > 24 { if len(candidates) > 24 {
candidates = candidates[:24] candidates = candidates[:24]
} }