Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -672,6 +672,20 @@ func cleanProductPricingFilterValues(values []string) []string {
|
||||
return clean
|
||||
}
|
||||
|
||||
func isProductCodeOnlyPricingFilter(filters ProductPricingFilters) bool {
|
||||
return len(cleanProductPricingFilterValues(filters.ProductCode)) > 0 &&
|
||||
strings.TrimSpace(filters.Search) == "" &&
|
||||
len(cleanProductPricingFilterValues(filters.BrandGroup)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.AskiliYan)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.Kategori)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.UrunIlkGrubu)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.UrunAnaGrubu)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.UrunAltGrubu)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.Icerik)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.Karisim)) == 0 &&
|
||||
len(cleanProductPricingFilterValues(filters.Marka)) == 0
|
||||
}
|
||||
|
||||
func GetProductPricingPage(ctx context.Context, page int, limit int, filters ProductPricingFilters, includeTotal bool, sortBy string, descending bool) (ProductPricingPage, error) {
|
||||
result := ProductPricingPage{
|
||||
Rows: []models.ProductPricing{},
|
||||
@@ -751,8 +765,28 @@ func GetProductPricingPage(ctx context.Context, page int, limit int, filters Pro
|
||||
}, " OR ")+")")
|
||||
}
|
||||
whereSQL := strings.Join(whereParts, " AND ")
|
||||
cleanProductCodes := cleanProductPricingFilterValues(filters.ProductCode)
|
||||
productCodeOnlyFilter := isProductCodeOnlyPricingFilter(filters)
|
||||
|
||||
if includeTotal {
|
||||
if includeTotal && productCodeOnlyFilter {
|
||||
result.TotalCount = len(cleanProductCodes)
|
||||
if result.TotalCount == 0 {
|
||||
result.TotalPages = 0
|
||||
result.Page = 1
|
||||
return result, nil
|
||||
}
|
||||
totalPages := int(math.Ceil(float64(result.TotalCount) / float64(limit)))
|
||||
if totalPages <= 0 {
|
||||
totalPages = 1
|
||||
}
|
||||
if page > totalPages {
|
||||
page = totalPages
|
||||
offset = (page - 1) * limit
|
||||
}
|
||||
result.Page = page
|
||||
result.Limit = limit
|
||||
result.TotalPages = totalPages
|
||||
} else if includeTotal {
|
||||
countQuery := `
|
||||
SELECT COUNT(DISTINCT LTRIM(RTRIM(ProductCode)))
|
||||
FROM ProductFilterWithDescription('TR')
|
||||
@@ -789,7 +823,7 @@ func GetProductPricingPage(ctx context.Context, page int, limit int, filters Pro
|
||||
|
||||
// Stage 1: fetch only paged products first. Exact product-code filters do not
|
||||
// need the stock sort temp table here; detailed metrics are fetched below.
|
||||
productCodeFastPath := len(cleanProductPricingFilterValues(filters.ProductCode)) > 0
|
||||
productCodeFastPath := len(cleanProductCodes) > 0
|
||||
sortBy = strings.TrimSpace(sortBy)
|
||||
orderDir := "DESC"
|
||||
if !descending {
|
||||
@@ -876,7 +910,72 @@ func GetProductPricingPage(ctx context.Context, page int, limit int, filters Pro
|
||||
OFFSET ` + strconv.Itoa(offset) + ` ROWS
|
||||
FETCH NEXT ` + strconv.Itoa(limit) + ` ROWS ONLY;
|
||||
`
|
||||
if productCodeFastPath {
|
||||
if productCodeOnlyFilter {
|
||||
valueRows := make([]string, 0, len(cleanProductCodes))
|
||||
directArgs := make([]any, 0, len(cleanProductCodes))
|
||||
for i, code := range cleanProductCodes {
|
||||
paramName := "@p" + strconv.Itoa(i+1)
|
||||
valueRows = append(valueRows, "("+paramName+")")
|
||||
directArgs = append(directArgs, code)
|
||||
}
|
||||
args = directArgs
|
||||
productQuery = `
|
||||
WITH req_codes AS (
|
||||
SELECT DISTINCT LTRIM(RTRIM(v.ProductCode)) AS ProductCode
|
||||
FROM (VALUES ` + strings.Join(valueRows, ",") + `) v(ProductCode)
|
||||
WHERE LEN(LTRIM(RTRIM(v.ProductCode))) > 0
|
||||
),
|
||||
attr AS (
|
||||
SELECT
|
||||
LTRIM(RTRIM(a.ItemCode)) AS ProductCode,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 45 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS AskiliYan,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 44 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS Kategori,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 42 THEN LTRIM(RTRIM(a.AttributeCode)) ELSE '' END) AS UrunIlkGrubuCode,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 42 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS UrunIlkGrubu,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 1 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS UrunAnaGrubu,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 2 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS UrunAltGrubu,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 41 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS Icerik,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 29 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS Karisim,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 10 THEN COALESCE(NULLIF(LTRIM(RTRIM(d.AttributeDescription)), ''), LTRIM(RTRIM(a.AttributeCode))) ELSE '' END) AS Marka,
|
||||
MAX(CASE WHEN a.AttributeTypeCode = 10 THEN LTRIM(RTRIM(a.AttributeCode)) ELSE '' END) AS BrandCode
|
||||
FROM dbo.prItemAttribute a WITH(NOLOCK)
|
||||
INNER JOIN req_codes rc
|
||||
ON rc.ProductCode = LTRIM(RTRIM(a.ItemCode))
|
||||
LEFT JOIN dbo.cdItemAttributeDesc d WITH(NOLOCK)
|
||||
ON d.ItemTypeCode = a.ItemTypeCode
|
||||
AND d.AttributeTypeCode = a.AttributeTypeCode
|
||||
AND d.AttributeCode = a.AttributeCode
|
||||
AND d.LangCode = 'TR'
|
||||
WHERE a.ItemTypeCode = 1
|
||||
AND a.AttributeTypeCode IN (1,2,10,29,41,42,44,45)
|
||||
GROUP BY LTRIM(RTRIM(a.ItemCode))
|
||||
)
|
||||
SELECT
|
||||
rc.ProductCode,
|
||||
CAST('' AS NVARCHAR(100)) AS BrandGroupSec,
|
||||
COALESCE(attr.AskiliYan, '') AS AskiliYan,
|
||||
COALESCE(attr.Kategori, '') AS Kategori,
|
||||
COALESCE(attr.UrunIlkGrubu, '') AS UrunIlkGrubu,
|
||||
COALESCE(attr.UrunAnaGrubu, '') AS UrunAnaGrubu,
|
||||
COALESCE(attr.UrunAltGrubu, '') AS UrunAltGrubu,
|
||||
COALESCE(attr.Icerik, '') AS Icerik,
|
||||
COALESCE(attr.Karisim, '') AS Karisim,
|
||||
COALESCE(attr.Marka, '') AS Marka,
|
||||
COALESCE(attr.BrandCode, '') AS BrandCode
|
||||
FROM req_codes rc
|
||||
INNER JOIN dbo.cdItem ci WITH(NOLOCK)
|
||||
ON ci.ItemTypeCode = 1
|
||||
AND LTRIM(RTRIM(ci.ItemCode)) = rc.ProductCode
|
||||
LEFT JOIN attr
|
||||
ON attr.ProductCode = rc.ProductCode
|
||||
WHERE ISNULL(ci.IsBlocked, 0) = 0
|
||||
AND LEN(LTRIM(RTRIM(ci.ItemCode))) = 13
|
||||
AND COALESCE(attr.UrunIlkGrubuCode, '') IN ('SERI', 'AKSESUAR')
|
||||
ORDER BY rc.ProductCode ASC
|
||||
OFFSET ` + strconv.Itoa(offset) + ` ROWS
|
||||
FETCH NEXT ` + strconv.Itoa(limit) + ` ROWS ONLY;
|
||||
`
|
||||
} else if productCodeFastPath {
|
||||
productQuery = `
|
||||
IF OBJECT_ID('tempdb..#req_codes') IS NOT NULL DROP TABLE #req_codes;
|
||||
|
||||
|
||||
@@ -258,8 +258,9 @@ norm AS (
|
||||
COALESCE(price, 0) AS price
|
||||
FROM input
|
||||
),
|
||||
-- Prefer PG's authoritative variant dimension table (mmitem_dim). Fall back to cache table if needed.
|
||||
dims_mmitem_dim AS (
|
||||
-- Only PG's authoritative variant dimension table may drive delta writes.
|
||||
-- Do not union sdprc/cache dimensions; stale rows there can re-create wrong variant keys.
|
||||
dims AS (
|
||||
SELECT
|
||||
norm.product_code AS product_code,
|
||||
md.val1::bigint AS dim1,
|
||||
@@ -275,41 +276,7 @@ norm AS (
|
||||
AND COALESCE(md.is_active, TRUE) = TRUE
|
||||
WHERE md.val1 IS NOT NULL
|
||||
AND md.val1 > 0
|
||||
GROUP BY norm.product_code, md.val1, md.val2
|
||||
),
|
||||
dims_cache_table AS (
|
||||
SELECT
|
||||
NULLIF(BTRIM(c.product_code), '') AS product_code,
|
||||
c.dim1::bigint AS dim1,
|
||||
c.dim3::bigint AS dim3
|
||||
FROM mk_mmitem_dim_combo c
|
||||
JOIN norm
|
||||
ON norm.product_code = c.product_code
|
||||
WHERE c.dim1 IS NOT NULL
|
||||
),
|
||||
dims_cache AS (
|
||||
SELECT product_code, dim1, dim3 FROM dims_mmitem_dim
|
||||
UNION
|
||||
SELECT product_code, dim1, dim3 FROM dims_cache_table
|
||||
),
|
||||
dims_sdprc AS (
|
||||
SELECT
|
||||
norm.product_code AS product_code,
|
||||
s.dim1 AS dim1,
|
||||
s.dim3 AS dim3
|
||||
FROM norm
|
||||
JOIN mmitem mm
|
||||
ON mm.code = norm.product_code
|
||||
JOIN sdprc s
|
||||
ON s.mmitem_id = mm.id
|
||||
WHERE s.dim1 IS NOT NULL
|
||||
AND s.dim1 > 0
|
||||
GROUP BY norm.product_code, s.dim1, s.dim3
|
||||
),
|
||||
dims AS (
|
||||
SELECT product_code, dim1, dim3 FROM dims_cache
|
||||
UNION
|
||||
SELECT product_code, dim1, dim3 FROM dims_sdprc
|
||||
GROUP BY norm.product_code, md.val1, md.val3
|
||||
),
|
||||
mapped AS (
|
||||
SELECT
|
||||
|
||||
@@ -431,37 +431,6 @@ WHERE is_active = TRUE
|
||||
return out
|
||||
}
|
||||
|
||||
loadDimCombosFromCache := func(productCode string) ([]dimCombo, error) {
|
||||
productCode = strings.TrimSpace(productCode)
|
||||
if productCode == "" {
|
||||
return nil, nil
|
||||
}
|
||||
rows, err := pgTx.QueryContext(ctx, `
|
||||
SELECT dim1, dim3
|
||||
FROM mk_mmitem_dim_combo
|
||||
WHERE product_code = $1
|
||||
ORDER BY dim1, dim3_key
|
||||
`, productCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
out := make([]dimCombo, 0, 32)
|
||||
for rows.Next() {
|
||||
var d1 int64
|
||||
var d3 sql.NullInt64
|
||||
if err := rows.Scan(&d1, &d3); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d1 <= 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, dimCombo{Dim1: d1, Dim3: d3})
|
||||
}
|
||||
return out, rows.Err()
|
||||
}
|
||||
|
||||
parseDimID := func(s string) (int64, bool) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
@@ -812,7 +781,7 @@ filtered AS (
|
||||
AND price > 0
|
||||
),
|
||||
grouped AS (
|
||||
-- Ensure one row per business key to avoid unique violations under strict constraints (e.g. uq_sdprc_3).
|
||||
-- Ensure one row per business key to avoid unique violations under strict constraints.
|
||||
SELECT
|
||||
sdprcgrp_id,
|
||||
currency AS crn,
|
||||
@@ -822,16 +791,35 @@ grouped AS (
|
||||
FROM filtered
|
||||
GROUP BY sdprcgrp_id, currency, dim1, dim3
|
||||
),
|
||||
upserted AS (
|
||||
updated AS (
|
||||
UPDATE sdprc s
|
||||
SET prc = g.prc,
|
||||
zlins_dttm = now()
|
||||
FROM grouped g
|
||||
WHERE s.mmitem_id = $2::bigint
|
||||
AND s.sdprcgrp_id = g.sdprcgrp_id
|
||||
AND s.crn = g.crn
|
||||
AND s.dim1 = g.dim1
|
||||
AND COALESCE(s.dim3, 0) = COALESCE(g.dim3, 0)
|
||||
AND s.prc IS DISTINCT FROM g.prc
|
||||
RETURNING 1
|
||||
),
|
||||
inserted AS (
|
||||
INSERT INTO sdprc (mmitem_id, sdprcgrp_id, crn, dim1, dim3, prc, zlins_dttm)
|
||||
SELECT $2::bigint, g.sdprcgrp_id, g.crn, g.dim1, g.dim3, g.prc, now()
|
||||
FROM grouped g
|
||||
ON CONFLICT ON CONSTRAINT uq_sdprc_3
|
||||
DO UPDATE SET prc = EXCLUDED.prc, zlins_dttm = EXCLUDED.zlins_dttm
|
||||
WHERE sdprc.prc IS DISTINCT FROM EXCLUDED.prc
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM sdprc s
|
||||
WHERE s.mmitem_id = $2::bigint
|
||||
AND s.sdprcgrp_id = g.sdprcgrp_id
|
||||
AND s.crn = g.crn
|
||||
AND s.dim1 = g.dim1
|
||||
AND COALESCE(s.dim3, 0) = COALESCE(g.dim3, 0)
|
||||
)
|
||||
RETURNING 1
|
||||
)
|
||||
SELECT COUNT(*)::int FROM upserted;
|
||||
SELECT ((SELECT COUNT(*) FROM updated) + (SELECT COUNT(*) FROM inserted))::int;
|
||||
`
|
||||
var inserted int
|
||||
if err := pgTx.QueryRowContext(ctx, q, raw, mmItemID).Scan(&inserted); err != nil {
|
||||
@@ -1083,28 +1071,8 @@ VALUES (
|
||||
_ = upsertDimCombosCache(code, dims) // best-effort cache fill
|
||||
}
|
||||
|
||||
// 2) Cache fallback (fast).
|
||||
cacheStarted := time.Now()
|
||||
if len(dims) == 0 {
|
||||
cached, cacheErr := loadDimCombosFromCache(code)
|
||||
if cacheErr == nil && len(cached) > 0 {
|
||||
dims = cached
|
||||
logger.Info("save:pg:dims:cache:hit",
|
||||
"product_code", code,
|
||||
"dims", len(dims),
|
||||
"duration_ms", time.Since(cacheStarted).Milliseconds(),
|
||||
)
|
||||
} else if cacheErr != nil {
|
||||
logger.Error("save:pg:dims:cache-load:error", "product_code", code, "err", cacheErr)
|
||||
} else {
|
||||
logger.Info("save:pg:dims:cache:miss",
|
||||
"product_code", code,
|
||||
"duration_ms", time.Since(cacheStarted).Milliseconds(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 3) Last resort: MSSQL stock tokens (legacy).
|
||||
// 2) Last resort: MSSQL stock tokens, then seed mmitem_dim. Do not use
|
||||
// mk_mmitem_dim_combo as a write source; stale cache rows can create wrong keys.
|
||||
if len(dims) == 0 {
|
||||
d, err := loadDimsFromMssqlStock(code)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user