From b7131ac6ce7023e2040dc8bf3f7c51b4f62971d5 Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Fri, 28 Aug 2026 20:44:58 +0300 Subject: [PATCH] Fix product performance grouped JSON build --- svc/queries/production_product_costing.go | 79 +++++++++++++------ .../production_product_costing_test.go | 32 ++++++++ svc/routes/production_product_costing.go | 22 ++++-- 3 files changed, 102 insertions(+), 31 deletions(-) create mode 100644 svc/queries/production_product_costing_test.go diff --git a/svc/queries/production_product_costing.go b/svc/queries/production_product_costing.go index 7dd11b5..7395d0a 100644 --- a/svc/queries/production_product_costing.go +++ b/svc/queries/production_product_costing.go @@ -799,42 +799,68 @@ type ArgeBaggiDepotEntryTotals struct { Yelek float64 } +type ArgeBaggiDepotEntryLookup struct { + WorkOrder string + ModelCode string +} + +func ArgeBaggiDepotEntryKey(workOrder string, modelCode string) string { + return strings.TrimSpace(workOrder) + "\x1f" + normalizeArgeBaggiModelCode(modelCode) +} + +func normalizeArgeBaggiModelCode(modelCode string) string { + normalizedModelCode := strings.ToUpper(strings.TrimSpace(modelCode)) + if separatorIndex := strings.LastIndex(normalizedModelCode, "-"); separatorIndex >= 0 && separatorIndex+1 < len(normalizedModelCode) { + return strings.TrimSpace(normalizedModelCode[separatorIndex+1:]) + } + return normalizedModelCode +} + // GetArgeBaggiDepotEntryTotals returns operation 985/986/987 quantities keyed -// by production work order. The requested work orders are queried in one batch -// to avoid an ArgeBAGGI round trip for every list row. -func GetArgeBaggiDepotEntryTotals(ctx context.Context, argeDB *sql.DB, workOrders []string) (map[string]ArgeBaggiDepotEntryTotals, error) { +// by production work order AND product model. A row contributes only when both +// MN.model_adi (work order) and MD.model_code (model) match the requested pair. +func GetArgeBaggiDepotEntryTotals(ctx context.Context, argeDB *sql.DB, lookups []ArgeBaggiDepotEntryLookup) (map[string]ArgeBaggiDepotEntryTotals, error) { out := make(map[string]ArgeBaggiDepotEntryTotals) - if argeDB == nil || len(workOrders) == 0 { + if argeDB == nil || len(lookups) == 0 { return out, nil } - seen := make(map[string]struct{}, len(workOrders)) - values := make([]string, 0, len(workOrders)) - for _, raw := range workOrders { - value := strings.TrimSpace(raw) - if value == "" || value == "0" { + seen := make(map[string]struct{}, len(lookups)) + values := make([]ArgeBaggiDepotEntryLookup, 0, len(lookups)) + for _, lookup := range lookups { + workOrder := strings.TrimSpace(lookup.WorkOrder) + modelCode := normalizeArgeBaggiModelCode(lookup.ModelCode) + if workOrder == "" || workOrder == "0" || modelCode == "" { continue } - if _, exists := seen[value]; exists { + key := ArgeBaggiDepotEntryKey(workOrder, modelCode) + if _, exists := seen[key]; exists { continue } - seen[value] = struct{}{} - values = append(values, value) + seen[key] = struct{}{} + values = append(values, ArgeBaggiDepotEntryLookup{WorkOrder: workOrder, ModelCode: modelCode}) } if len(values) == 0 { return out, nil } - placeholders := make([]string, 0, len(values)) - args := make([]any, 0, len(values)) + valueRows := make([]string, 0, len(values)) + args := make([]any, 0, len(values)*2) for i, value := range values { - placeholders = append(placeholders, fmt.Sprintf("@p%d", i+1)) - args = append(args, value) + workOrderParam := i*2 + 1 + modelCodeParam := workOrderParam + 1 + valueRows = append(valueRows, fmt.Sprintf("(@p%d, @p%d)", workOrderParam, modelCodeParam)) + args = append(args, value.WorkOrder, value.ModelCode) } sqlText := fmt.Sprintf(` +WITH RequestedPairs (IsEmriNo, ModelKodu) AS ( + SELECT V.IsEmriNo, V.ModelKodu + FROM (VALUES %s) V (IsEmriNo, ModelKodu) +) SELECT - LTRIM(RTRIM(CONVERT(VARCHAR(64), MN.model_adi))) AS IsEmriNo, + RP.IsEmriNo, + RP.ModelKodu, SUM(CASE WHEN O.oper_num = 985 THEN CONVERT(float, B.adet) ELSE 0 END) AS CeketDepoGiris, SUM(CASE WHEN O.oper_num = 986 THEN CONVERT(float, B.adet) ELSE 0 END) AS PantolonDepoGiris, SUM(CASE WHEN O.oper_num = 987 THEN CONVERT(float, B.adet) ELSE 0 END) AS YelekDepoGiris @@ -846,12 +872,14 @@ INNER JOIN dbo.model_num MN ON I.model_num = MN.model_num INNER JOIN dbo.bant_no BN ON I.bant_num = BN.bant_no INNER JOIN dbo.model_oper MO ON O.oper_num = MO.oper_num AND MD.mdef_num = MO.model_num INNER JOIN dbo.model_oper_tkp_def TD ON MO.tkp_cik = TD.tkp_cik +INNER JOIN RequestedPairs RP + ON RP.IsEmriNo = LTRIM(RTRIM(CONVERT(VARCHAR(64), MN.model_adi))) + AND RP.ModelKodu = UPPER(LTRIM(RTRIM(CONVERT(VARCHAR(64), MD.model_code)))) WHERE B.tarih >= '2025-01-01' AND TD.tkp_cik IN (1,2,3,4,5) AND O.oper_num IN (985,986,987) - AND LTRIM(RTRIM(CONVERT(VARCHAR(64), MN.model_adi))) IN (%s) -GROUP BY LTRIM(RTRIM(CONVERT(VARCHAR(64), MN.model_adi))) -`, strings.Join(placeholders, ",")) +GROUP BY RP.IsEmriNo, RP.ModelKodu +`, strings.Join(valueRows, ",")) rows, err := argeDB.QueryContext(ctx, sqlText, args...) if err != nil { @@ -860,13 +888,18 @@ GROUP BY LTRIM(RTRIM(CONVERT(VARCHAR(64), MN.model_adi))) defer rows.Close() for rows.Next() { var workOrder string + var modelCode string var totals ArgeBaggiDepotEntryTotals - if err := rows.Scan(&workOrder, &totals.Ceket, &totals.Pantolon, &totals.Yelek); err != nil { + if err := rows.Scan(&workOrder, &modelCode, &totals.Ceket, &totals.Pantolon, &totals.Yelek); err != nil { return nil, err } - out[strings.TrimSpace(workOrder)] = totals + out[ArgeBaggiDepotEntryKey(workOrder, modelCode)] = totals } - return out, rows.Err() + if err := rows.Err(); err != nil { + return nil, err + } + + return out, nil } func GetProductionHasCostProducts( diff --git a/svc/queries/production_product_costing_test.go b/svc/queries/production_product_costing_test.go new file mode 100644 index 0000000..53e2a97 --- /dev/null +++ b/svc/queries/production_product_costing_test.go @@ -0,0 +1,32 @@ +package queries + +import "testing" + +func TestNormalizeArgeBaggiModelCode(t *testing.T) { + tests := []struct { + name string + modelCode string + want string + }{ + {name: "tam urun kodu", modelCode: "M038-TKX60788", want: "TKX60788"}, + {name: "arge model kodu", modelCode: "TKX60788", want: "TKX60788"}, + {name: "bosluk ve kucuk harf", modelCode: " m038-tkx60788 ", want: "TKX60788"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := normalizeArgeBaggiModelCode(tt.modelCode) + if got != tt.want { + t.Fatalf("normalizeArgeBaggiModelCode(%q) = %q, want %q", tt.modelCode, got, tt.want) + } + }) + } +} + +func TestArgeBaggiDepotEntryKeyNormalizesPair(t *testing.T) { + got := ArgeBaggiDepotEntryKey(" 217655 ", " m038-ckk60281 ") + want := "217655\x1fCKK60281" + if got != want { + t.Fatalf("ArgeBaggiDepotEntryKey() = %q, want %q", got, want) + } +} diff --git a/svc/routes/production_product_costing.go b/svc/routes/production_product_costing.go index 7e28e7f..0ad19f6 100644 --- a/svc/routes/production_product_costing.go +++ b/svc/routes/production_product_costing.go @@ -159,18 +159,21 @@ func GetProductionNoCostProductsHandler(w http.ResponseWriter, r *http.Request) } if argeDB := db.GetArgeBaggiDB(); argeDB != nil && len(list) > 0 { - workOrders := make([]string, 0, len(list)) + lookups := make([]queries.ArgeBaggiDepotEntryLookup, 0, len(list)) for i := range list { - workOrders = append(workOrders, list[i].UrtSiparisNo) + lookups = append(lookups, queries.ArgeBaggiDepotEntryLookup{ + WorkOrder: list[i].UrtSiparisNo, + ModelCode: list[i].MModelKodu, + }) } argeCtx, cancelArge := context.WithTimeout(r.Context(), 15*time.Second) - totalsByWorkOrder, argeErr := queries.GetArgeBaggiDepotEntryTotals(argeCtx, argeDB, workOrders) + totalsByWorkOrderModel, argeErr := queries.GetArgeBaggiDepotEntryTotals(argeCtx, argeDB, lookups) cancelArge() if argeErr != nil { log.Printf("[ProductionNoCost] ArgeBAGGI enrichment error: %v", argeErr) } else { for i := range list { - totals := totalsByWorkOrder[strings.TrimSpace(list[i].UrtSiparisNo)] + totals := totalsByWorkOrderModel[queries.ArgeBaggiDepotEntryKey(list[i].UrtSiparisNo, list[i].MModelKodu)] list[i].CeketDepoGiris = totals.Ceket list[i].PantolonDepoGiris = totals.Pantolon list[i].YelekDepoGiris = totals.Yelek @@ -244,18 +247,21 @@ func GetProductionHasCostProductsHandler(w http.ResponseWriter, r *http.Request) } if argeDB := db.GetArgeBaggiDB(); argeDB != nil && len(list) > 0 { - workOrders := make([]string, 0, len(list)) + lookups := make([]queries.ArgeBaggiDepotEntryLookup, 0, len(list)) for i := range list { - workOrders = append(workOrders, list[i].UrtSiparisNo) + lookups = append(lookups, queries.ArgeBaggiDepotEntryLookup{ + WorkOrder: list[i].UrtSiparisNo, + ModelCode: list[i].UrunKodu, + }) } argeCtx, cancelArge := context.WithTimeout(r.Context(), 15*time.Second) - totalsByWorkOrder, argeErr := queries.GetArgeBaggiDepotEntryTotals(argeCtx, argeDB, workOrders) + totalsByWorkOrderModel, argeErr := queries.GetArgeBaggiDepotEntryTotals(argeCtx, argeDB, lookups) cancelArge() if argeErr != nil { log.Printf("[ProductionHasCost] ArgeBAGGI enrichment error: %v", argeErr) } else { for i := range list { - totals := totalsByWorkOrder[strings.TrimSpace(list[i].UrtSiparisNo)] + totals := totalsByWorkOrderModel[queries.ArgeBaggiDepotEntryKey(list[i].UrtSiparisNo, list[i].UrunKodu)] list[i].CeketDepoGiris = totals.Ceket list[i].PantolonDepoGiris = totals.Pantolon list[i].YelekDepoGiris = totals.Yelek