From 79ffe7862c498a6d4a87965fd259d9e7cabebb04 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Wed, 24 Jun 2026 12:04:33 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/product_series_auto_scheduler.go | 84 ++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 18 deletions(-) diff --git a/svc/product_series_auto_scheduler.go b/svc/product_series_auto_scheduler.go index 3a712b6..f739e73 100644 --- a/svc/product_series_auto_scheduler.go +++ b/svc/product_series_auto_scheduler.go @@ -582,25 +582,38 @@ func productSeriesApplyVariant(ctx context.Context, pg *sql.DB, v productSeriesA } if fallbackID > 0 { if !productSeriesFallbackOverrideExisting() { - // 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 { - if productSeriesFallbackLogEnabled() { - log.Printf("[ProductSeriesFallback] already_exists product=%s color=%s dim3=%s fallback=%s(%d)", strings.TrimSpace(v.ProductCode), strings.TrimSpace(v.ColorCode), strings.TrimSpace(v.Dim3Code), strings.TrimSpace(fallbackCode), fallbackID) - } - // keep existing manual/previous assignment; nothing to do - return 0, 0, nil + // By default we don't override existing assignments. However, if the existing assignment(s) + // are no longer compatible with current stock (e.g., stock sold out), keeping them causes + // stale/wrong series to stick forever (already_exists). In that case, we should replace + // them with fallback=1. + existingIDs, err := productSeriesLoadExistingSeriesIDs(ctx, pg, mmitemID, dim1ID, dim3ID) + if err != nil { + return 0, 1, err } - if checkErr != nil && checkErr != sql.ErrNoRows { - return 0, 1, checkErr + if len(existingIDs) > 0 { + anyStillValid := false + for _, sid := range existingIDs { + // If we can't find the rule definition in memory, assume it's a manual/unknown mapping and keep it. + rule, ok := productSeriesFindRuleByID(rules, sid) + if !ok { + anyStillValid = true + break + } + if productSeriesCanConsume(v.SizeQty, rule) { + anyStillValid = true + break + } + } + if anyStillValid { + if productSeriesFallbackLogEnabled() { + log.Printf("[ProductSeriesFallback] already_exists product=%s color=%s dim3=%s fallback=%s(%d)", strings.TrimSpace(v.ProductCode), strings.TrimSpace(v.ColorCode), strings.TrimSpace(v.Dim3Code), strings.TrimSpace(fallbackCode), fallbackID) + } + // keep existing manual/previous assignment; nothing to do + return 0, 0, nil + } + if productSeriesFallbackLogEnabled() { + log.Printf("[ProductSeriesFallback] existing_invalid_apply product=%s color=%s dim3=%s fallback=%s(%d)", strings.TrimSpace(v.ProductCode), strings.TrimSpace(v.ColorCode), strings.TrimSpace(v.Dim3Code), strings.TrimSpace(fallbackCode), fallbackID) + } } } if productSeriesFallbackLogEnabled() { @@ -650,6 +663,41 @@ VALUES ($1, $2, $3, $4) return len(selected), 0, nil } +func productSeriesFindRuleByID(rules []productSeriesAutoRule, seriesID int64) (productSeriesAutoRule, bool) { + for _, r := range rules { + if r.SeriesID == seriesID { + return r, true + } + } + return productSeriesAutoRule{}, false +} + +func productSeriesLoadExistingSeriesIDs(ctx context.Context, pg *sql.DB, mmitemID int64, dim1ID int64, dim3ID sql.NullInt64) ([]int64, error) { + rows, err := pg.QueryContext(ctx, ` +SELECT seri_id +FROM zbggseri +WHERE mmitem_id=$1 + AND dim1=$2 + AND (($3::bigint IS NULL AND dim3 IS NULL) OR dim3=$3::bigint) +ORDER BY seri_id +`, mmitemID, dim1ID, nullableInt64ForAuto(dim3ID)) + if err != nil { + return nil, err + } + defer rows.Close() + out := []int64{} + for rows.Next() { + var id int64 + if err := rows.Scan(&id); err != nil { + return nil, err + } + if id > 0 { + out = append(out, id) + } + } + return out, rows.Err() +} + func productSeriesLoadMSSQLStockVariants(ctx context.Context) (map[string]productSeriesAutoVariant, int, error) { rows, err := db.MssqlDB.QueryContext(ctx, queries.GetProductSeriesStockRowsQuery, "", "", "", "", "", "", "", "", "", "0", "",