package queries import ( "bssapp-backend/db" "bssapp-backend/models" "context" "database/sql" "fmt" "strings" "time" ) func GetProductPricingList(ctx context.Context, limit int, offset int) ([]models.ProductPricing, error) { if limit <= 0 { limit = 500 } if offset < 0 { offset = 0 } query := ` WITH base_products AS ( SELECT LTRIM(RTRIM(ProductCode)) AS ProductCode, COALESCE(LTRIM(RTRIM(ProductAtt45Desc)), '') AS AskiliYan, COALESCE(LTRIM(RTRIM(ProductAtt44Desc)), '') AS Kategori, COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '') AS UrunIlkGrubu, COALESCE(LTRIM(RTRIM(ProductAtt01Desc)), '') AS UrunAnaGrubu, COALESCE(LTRIM(RTRIM(ProductAtt02Desc)), '') AS UrunAltGrubu, COALESCE(LTRIM(RTRIM(ProductAtt41Desc)), '') AS Icerik, COALESCE(LTRIM(RTRIM(ProductAtt29Desc)), '') AS Karisim, COALESCE(LTRIM(RTRIM(ProductAtt10Desc)), '') AS Marka FROM ProductFilterWithDescription('TR') WHERE ProductAtt42 IN ('SERI', 'AKSESUAR') AND IsBlocked = 0 AND LEN(LTRIM(RTRIM(ProductCode))) = 13 ), paged_products AS ( SELECT bp.ProductCode FROM base_products bp ORDER BY bp.ProductCode OFFSET %d ROWS FETCH NEXT %d ROWS ONLY ), latest_base_price AS ( SELECT LTRIM(RTRIM(b.ItemCode)) AS ItemCode, CAST(b.Price AS DECIMAL(18, 2)) AS CostPrice, CONVERT(VARCHAR(10), b.PriceDate, 23) AS LastPricingDate, ROW_NUMBER() OVER ( PARTITION BY LTRIM(RTRIM(b.ItemCode)) ORDER BY b.PriceDate DESC, b.LastUpdatedDate DESC ) AS rn FROM prItemBasePrice b WHERE b.ItemTypeCode = 1 AND b.BasePriceCode = 1 AND LTRIM(RTRIM(b.CurrencyCode)) = 'USD' AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(b.ItemCode)) ) ), stock_entry_dates AS ( SELECT LTRIM(RTRIM(s.ItemCode)) AS ItemCode, CONVERT(VARCHAR(10), MAX(s.OperationDate), 23) AS StockEntryDate FROM trStock s WITH(NOLOCK) WHERE s.ItemTypeCode = 1 AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13 AND s.In_Qty1 > 0 AND LTRIM(RTRIM(s.WarehouseCode)) IN ( '1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28', '1-0-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3', '1-0-33','101','1-014','1-0-49','1-0-36' ) AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(s.ItemCode)) ) GROUP BY LTRIM(RTRIM(s.ItemCode)) ), stock_base AS ( SELECT LTRIM(RTRIM(s.ItemCode)) AS ItemCode, SUM(s.In_Qty1 - s.Out_Qty1) AS InventoryQty1 FROM trStock s WITH(NOLOCK) WHERE s.ItemTypeCode = 1 AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13 AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(s.ItemCode)) ) GROUP BY LTRIM(RTRIM(s.ItemCode)) ), pick_base AS ( SELECT LTRIM(RTRIM(p.ItemCode)) AS ItemCode, SUM(p.Qty1) AS PickingQty1 FROM PickingStates p WHERE p.ItemTypeCode = 1 AND LEN(LTRIM(RTRIM(p.ItemCode))) = 13 AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(p.ItemCode)) ) GROUP BY LTRIM(RTRIM(p.ItemCode)) ), reserve_base AS ( SELECT LTRIM(RTRIM(r.ItemCode)) AS ItemCode, SUM(r.Qty1) AS ReserveQty1 FROM ReserveStates r WHERE r.ItemTypeCode = 1 AND LEN(LTRIM(RTRIM(r.ItemCode))) = 13 AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(r.ItemCode)) ) GROUP BY LTRIM(RTRIM(r.ItemCode)) ), disp_base AS ( SELECT LTRIM(RTRIM(d.ItemCode)) AS ItemCode, SUM(d.Qty1) AS DispOrderQty1 FROM DispOrderStates d WHERE d.ItemTypeCode = 1 AND LEN(LTRIM(RTRIM(d.ItemCode))) = 13 AND EXISTS ( SELECT 1 FROM paged_products pp WHERE pp.ProductCode = LTRIM(RTRIM(d.ItemCode)) ) GROUP BY LTRIM(RTRIM(d.ItemCode)) ), stock_totals AS ( SELECT pp.ProductCode AS ItemCode, CAST(ROUND( ISNULL(sb.InventoryQty1, 0) - ISNULL(pb.PickingQty1, 0) - ISNULL(rb.ReserveQty1, 0) - ISNULL(db.DispOrderQty1, 0) , 2) AS DECIMAL(18, 2)) AS StockQty FROM paged_products pp LEFT JOIN stock_base sb ON sb.ItemCode = pp.ProductCode LEFT JOIN pick_base pb ON pb.ItemCode = pp.ProductCode LEFT JOIN reserve_base rb ON rb.ItemCode = pp.ProductCode LEFT JOIN disp_base db ON db.ItemCode = pp.ProductCode ) SELECT bp.ProductCode AS ProductCode, COALESCE(lp.CostPrice, 0) AS CostPrice, COALESCE(st.StockQty, 0) AS StockQty, COALESCE(se.StockEntryDate, '') AS StockEntryDate, COALESCE(lp.LastPricingDate, '') AS LastPricingDate, bp.AskiliYan, bp.Kategori, bp.UrunIlkGrubu, bp.UrunAnaGrubu, bp.UrunAltGrubu, bp.Icerik, bp.Karisim, bp.Marka FROM paged_products pp INNER JOIN base_products bp ON bp.ProductCode = pp.ProductCode LEFT JOIN latest_base_price lp ON lp.ItemCode = bp.ProductCode AND lp.rn = 1 LEFT JOIN stock_entry_dates se ON se.ItemCode = bp.ProductCode LEFT JOIN stock_totals st ON st.ItemCode = bp.ProductCode ORDER BY bp.ProductCode; ` query = fmt.Sprintf(query, offset, limit) var ( rows *sql.Rows rowsErr error ) for attempt := 1; attempt <= 3; attempt++ { var err error rows, err = db.MssqlDB.QueryContext(ctx, query) if err == nil { rowsErr = nil break } rowsErr = err if ctx.Err() != nil || !isTransientMSSQLNetworkError(err) || attempt == 3 { break } wait := time.Duration(attempt*300) * time.Millisecond select { case <-ctx.Done(): break case <-time.After(wait): } } if rowsErr != nil { return nil, rowsErr } defer rows.Close() var out []models.ProductPricing for rows.Next() { var item models.ProductPricing if err := rows.Scan( &item.ProductCode, &item.CostPrice, &item.StockQty, &item.StockEntryDate, &item.LastPricingDate, &item.AskiliYan, &item.Kategori, &item.UrunIlkGrubu, &item.UrunAnaGrubu, &item.UrunAltGrubu, &item.Icerik, &item.Karisim, &item.Marka, ); err != nil { return nil, err } out = append(out, item) } return out, nil } func isTransientMSSQLNetworkError(err error) bool { if err == nil { return false } e := strings.ToLower(err.Error()) return strings.Contains(e, "i/o timeout") || strings.Contains(e, "timeout") || strings.Contains(e, "wsarecv") || strings.Contains(e, "connection attempt failed") || strings.Contains(e, "no connection could be made") || strings.Contains(e, "broken pipe") || strings.Contains(e, "connection reset") }