ui: add B2B olmayan stok (orphans) page

This commit is contained in:
M_Kececi
2026-06-24 23:14:58 +03:00
parent 701cc5e439
commit aa100973b3
2 changed files with 57 additions and 32 deletions

View File

@@ -1168,19 +1168,22 @@ func productSeriesResolvePGVariant(ctx context.Context, pg *sql.DB, productCode,
dim3ID = sql.NullInt64{Int64: id, Valid: true}
}
// Only treat variants as ready if they exist in authoritative item-dim combos.
// Only treat variants as ready if they exist in authoritative item-dim combos (mmitem_dim).
// This prevents writing series assignments for stock variants that don't exist on the PG side.
dim3Key := int64(0)
if dim3ID.Valid {
if dim3ID.Valid && dim3ID.Int64 > 1000 {
dim3Key = dim3ID.Int64
}
var exists int
if err := pg.QueryRowContext(ctx, `
SELECT 1
FROM mk_mmitem_dim_combo
WHERE product_code=$1 AND dim1=$2 AND dim3_key=$3
FROM mmitem_dim
WHERE mmitem_id=$1
AND is_active=TRUE
AND val1=$2
AND COALESCE(CASE WHEN val3 IS NOT NULL AND val3 > 1000 THEN val3 ELSE 0 END, 0) = $3
LIMIT 1
`, strings.TrimSpace(productCode), dim1ID, dim3Key).Scan(&exists); err != nil {
`, mmitemID, dim1ID, dim3Key).Scan(&exists); err != nil {
if err == sql.ErrNoRows {
return 0, 0, sql.NullInt64{}, false, nil
}

View File

@@ -318,28 +318,34 @@ WHERE dim_column=$1 AND token = ANY($2)
return out, rows.Err()
}
// Load all known (product_code, dim1, dim3_key) combos for products in this response.
loadProductDimCombos := func(ctx context.Context, pg *sql.DB, productCodes []string) (map[string]struct{}, error) {
// Load authoritative combos from mmitem_dim (mmitem_id + val1(color) + val3(dim3 if any)).
// We do NOT rely on mk_mmitem_dim_combo here because that cache may be empty/stale.
loadProductDimCombos := func(ctx context.Context, pg *sql.DB, mmitemIDs []int64) (map[string]struct{}, error) {
out := map[string]struct{}{}
if len(productCodes) == 0 {
if len(mmitemIDs) == 0 {
return out, nil
}
rows, err := pg.QueryContext(ctx, `
SELECT product_code, dim1, dim3_key
FROM mk_mmitem_dim_combo
WHERE product_code = ANY($1)
`, pq.Array(productCodes))
SELECT
mmitem_id,
val1 AS dim1,
CASE WHEN val3 IS NOT NULL AND val3 > 1000 THEN val3 ELSE 0 END AS dim3_key
FROM mmitem_dim
WHERE mmitem_id = ANY($1)
AND is_active = TRUE
AND val1 IS NOT NULL
GROUP BY mmitem_id, val1, CASE WHEN val3 IS NOT NULL AND val3 > 1000 THEN val3 ELSE 0 END
`, pq.Array(mmitemIDs))
if err != nil {
return out, err
}
defer rows.Close()
for rows.Next() {
var code string
var dim1, dim3Key int64
if err := rows.Scan(&code, &dim1, &dim3Key); err != nil {
var mmitemID, dim1, dim3Key int64
if err := rows.Scan(&mmitemID, &dim1, &dim3Key); err != nil {
return out, err
}
key := fmt.Sprintf("%s|%d|%d", strings.TrimSpace(code), dim1, dim3Key)
key := fmt.Sprintf("%d|%d|%d", mmitemID, dim1, dim3Key)
out[key] = struct{}{}
}
return out, rows.Err()
@@ -349,7 +355,13 @@ WHERE product_code = ANY($1)
mmitemByCode, _ := loadMmitemIDs(ctx, pg, codes)
dim1ByToken, _ := loadDimTokenIDsStrict(ctx, pg, "dimval1", setToSortedSlice(colorSet))
dim3ByToken, _ := loadDimTokenIDsStrict(ctx, pg, "dimval3", setToSortedSlice(dim3Set))
combos, _ := loadProductDimCombos(ctx, pg, codes)
mmitemIDs := make([]int64, 0, len(mmitemByCode))
for _, c := range codes {
if id := mmitemByCode[c]; id > 0 {
mmitemIDs = append(mmitemIDs, id)
}
}
combos, _ := loadProductDimCombos(ctx, pg, mmitemIDs)
existing, _ := loadProductSeriesAssignments(ctx, pg, codes)
// Per-request cache for per-mmitem dimval3 inference to avoid repeated dfblob scans.
@@ -430,7 +442,7 @@ LIMIT 1
dim3Key = row.Dim3ID
}
if row.MappingReady {
_, ok := combos[fmt.Sprintf("%s|%d|%d", strings.TrimSpace(row.ProductCode), row.Dim1ID, dim3Key)]
_, ok := combos[fmt.Sprintf("%d|%d|%d", row.MmitemID, row.Dim1ID, dim3Key)]
row.MappingReady = ok
}
if !row.MappingReady {
@@ -609,27 +621,32 @@ WHERE dim_column=$1 AND token = ANY($2)
return out, rows.Err()
}
loadProductDimCombos := func(ctx context.Context, pg *sql.DB, productCodes []string) (map[string]struct{}, error) {
loadProductDimCombos := func(ctx context.Context, pg *sql.DB, mmitemIDs []int64) (map[string]struct{}, error) {
out := map[string]struct{}{}
if len(productCodes) == 0 {
if len(mmitemIDs) == 0 {
return out, nil
}
rows, err := pg.QueryContext(ctx, `
SELECT product_code, dim1, dim3_key
FROM mk_mmitem_dim_combo
WHERE product_code = ANY($1)
`, pq.Array(productCodes))
SELECT
mmitem_id,
val1 AS dim1,
CASE WHEN val3 IS NOT NULL AND val3 > 1000 THEN val3 ELSE 0 END AS dim3_key
FROM mmitem_dim
WHERE mmitem_id = ANY($1)
AND is_active = TRUE
AND val1 IS NOT NULL
GROUP BY mmitem_id, val1, CASE WHEN val3 IS NOT NULL AND val3 > 1000 THEN val3 ELSE 0 END
`, pq.Array(mmitemIDs))
if err != nil {
return out, err
}
defer rows.Close()
for rows.Next() {
var code string
var dim1, dim3Key int64
if err := rows.Scan(&code, &dim1, &dim3Key); err != nil {
var mmitemID, dim1, dim3Key int64
if err := rows.Scan(&mmitemID, &dim1, &dim3Key); err != nil {
return out, err
}
key := fmt.Sprintf("%s|%d|%d", strings.TrimSpace(code), dim1, dim3Key)
key := fmt.Sprintf("%d|%d|%d", mmitemID, dim1, dim3Key)
out[key] = struct{}{}
}
return out, rows.Err()
@@ -639,7 +656,13 @@ WHERE product_code = ANY($1)
mmitemByCode, _ := loadMmitemIDs(ctx, pg, codes)
dim1ByToken, _ := loadDimTokenIDsStrict(ctx, pg, "dimval1", setToSortedSlice(colorSet))
dim3ByToken, _ := loadDimTokenIDsStrict(ctx, pg, "dimval3", setToSortedSlice(dim3Set))
combos, _ := loadProductDimCombos(ctx, pg, codes)
mmitemIDs := make([]int64, 0, len(mmitemByCode))
for _, c := range codes {
if id := mmitemByCode[c]; id > 0 {
mmitemIDs = append(mmitemIDs, id)
}
}
combos, _ := loadProductDimCombos(ctx, pg, mmitemIDs)
// Per-request cache for per-mmitem dimval3 inference (dfblob scan).
inferCache := map[string]int64{}
@@ -720,8 +743,7 @@ LIMIT 1
if row.Dim3Code != "" {
dim3Key = row.Dim3ID
}
comboKey := fmt.Sprintf("%s|%d|%d", strings.TrimSpace(row.ProductCode), row.Dim1ID, dim3Key)
_, comboOK := combos[comboKey]
_, comboOK := combos[fmt.Sprintf("%d|%d|%d", row.MmitemID, row.Dim1ID, dim3Key)]
row.MappingReady = false
switch {
@@ -732,7 +754,7 @@ LIMIT 1
case row.Dim3Code != "" && row.Dim3ID <= 0:
row.MappingWarning = "B2B'de dim3 token eslesmesi yok (mk_dim_token_map: dimval3)"
case !comboOK:
row.MappingWarning = "B2B'de varyant kombosu yok (mk_mmitem_dim_combo)"
row.MappingWarning = "B2B'de varyant kombosu yok (mmitem_dim)"
default:
// Not an orphan; skip.
if baseReady && comboOK {