Files
bssapp/svc/queries/product_performance_sql.go
T

593 lines
26 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package queries
func productPerformanceSalesSQL() string {
return `
;WITH SalesLines AS (
SELECT
I.InvoiceLineID,
SalesDate = CAST(I.InvoiceDate AS date),
I.InvoiceHeaderID,
I.InvoiceNumber,
ProductCode = LTRIM(RTRIM(I.ItemCode)),
ColorCode = LTRIM(RTRIM(ISNULL(I.ColorCode, ''))),
YakaKodu = LTRIM(RTRIM(ISNULL(I.ItemDim2Code, ''))),
ItemDescription = dbo.HG_Temizlik(ISNULL((
SELECT ItemDescription
FROM cdItemDesc WITH(NOLOCK)
WHERE cdItemDesc.ItemTypeCode = I.ItemTypeCode
AND cdItemDesc.ItemCode = I.ItemCode
AND cdItemDesc.LangCode = 'TR'
), SPACE(0))),
UrunAnaGrubu = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdItemAttributeDesc WITH(NOLOCK)
WHERE cdItemAttributeDesc.ItemTypeCode = I.ItemTypeCode
AND cdItemAttributeDesc.AttributeTypeCode = 1
AND cdItemAttributeDesc.AttributeCode = (
SELECT TOP 1 AttributeCode
FROM prItemAttribute WITH(NOLOCK)
WHERE AttributeTypeCode = 1
AND prItemAttribute.ItemTypeCode = I.ItemTypeCode
AND prItemAttribute.ItemCode = I.ItemCode
)
AND cdItemAttributeDesc.LangCode = 'TR'
), SPACE(0))),
UrunAltGrubu = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdItemAttributeDesc WITH(NOLOCK)
WHERE cdItemAttributeDesc.ItemTypeCode = I.ItemTypeCode
AND cdItemAttributeDesc.AttributeTypeCode = 2
AND cdItemAttributeDesc.AttributeCode = (
SELECT TOP 1 AttributeCode
FROM prItemAttribute WITH(NOLOCK)
WHERE AttributeTypeCode = 2
AND prItemAttribute.ItemTypeCode = I.ItemTypeCode
AND prItemAttribute.ItemCode = I.ItemCode
)
AND cdItemAttributeDesc.LangCode = 'TR'
), SPACE(0))),
Kategori = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdItemAttributeDesc WITH(NOLOCK)
WHERE cdItemAttributeDesc.ItemTypeCode = I.ItemTypeCode
AND cdItemAttributeDesc.AttributeTypeCode = 42
AND cdItemAttributeDesc.AttributeCode = (
SELECT TOP 1 AttributeCode
FROM prItemAttribute WITH(NOLOCK)
WHERE AttributeTypeCode = 42
AND prItemAttribute.ItemTypeCode = I.ItemTypeCode
AND prItemAttribute.ItemCode = I.ItemCode
)
AND cdItemAttributeDesc.LangCode = 'TR'
), SPACE(0))),
UrunIlkGrubu = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdItemAttributeDesc WITH(NOLOCK)
WHERE cdItemAttributeDesc.ItemTypeCode = I.ItemTypeCode
AND cdItemAttributeDesc.AttributeTypeCode = 44
AND cdItemAttributeDesc.AttributeCode = (
SELECT TOP 1 AttributeCode
FROM prItemAttribute WITH(NOLOCK)
WHERE AttributeTypeCode = 44
AND prItemAttribute.ItemTypeCode = I.ItemTypeCode
AND prItemAttribute.ItemCode = I.ItemCode
)
AND cdItemAttributeDesc.LangCode = 'TR'
), SPACE(0))),
AskiliYan = dbo.HG_Temizlik(ISNULL((
SELECT TOP 1 ProductAtt45
FROM ProductAttributesFilter WITH(NOLOCK)
WHERE ProductAttributesFilter.ItemCode = I.ItemCode
), SPACE(0))),
ChannelCode = CASE
WHEN I.ProcessCode IN ('R') THEN dbo.HG_Temizlik(ISNULL((
SELECT OfficeDescription
FROM cdOfficeDesc WITH(NOLOCK)
WHERE cdOfficeDesc.OfficeCode = I.OfficeCode
AND cdOfficeDesc.LangCode = 'TR'
), SPACE(0)))
ELSE dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
WHERE cdCurrAccAttributeDesc.CurrAccTypeCode = 3
AND AttributeTypeCode = 1
AND CAF.CustomerAtt01 = AttributeCode
AND cdCurrAccAttributeDesc.LangCode = 'TR'
), SPACE(0)))
END,
CustomerCountry = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
WHERE cdCurrAccAttributeDesc.CurrAccTypeCode = 3
AND AttributeTypeCode = 5
AND CAF.CustomerAtt05 = AttributeCode
AND cdCurrAccAttributeDesc.LangCode = 'TR'
), SPACE(0))),
CustomerSegment = dbo.HG_Temizlik(ISNULL((
SELECT AttributeDescription
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
WHERE cdCurrAccAttributeDesc.CurrAccTypeCode = 3
AND AttributeTypeCode = 11
AND CAF.CustomerAtt11 = AttributeCode
AND cdCurrAccAttributeDesc.LangCode = 'TR'
), SPACE(0))),
CustomerCode = CASE
WHEN I.ProcessCode = 'R' THEN dbo.HG_Temizlik(ISNULL(I.StoreCode, ''))
ELSE dbo.HG_Temizlik(ISNULL(I.CurrAccCode, ''))
END,
CustomerDescription = CASE
WHEN I.ProcessCode = 'R' THEN dbo.HG_Temizlik(ISNULL((
SELECT CurrAccDescription
FROM cdCurrAccDesc WITH(NOLOCK)
WHERE cdCurrAccDesc.CurrAccTypeCode = 5
AND cdCurrAccDesc.CurrAccCode = I.StoreCode
AND cdCurrAccDesc.LangCode = 'TR'
), SPACE(0)))
ELSE dbo.HG_Temizlik(ISNULL((
SELECT CurrAccDescription
FROM cdCurrAccDesc WITH(NOLOCK)
WHERE cdCurrAccDesc.CurrAccTypeCode = I.CurrAccTypeCode
AND cdCurrAccDesc.CurrAccCode = I.CurrAccCode
AND cdCurrAccDesc.LangCode = 'TR'
), SPACE(0)))
END,
Qty1 = ISNULL(I.Qty1, 0),
TLAmount = ISNULL(I.Doc_Amount, 0) * ISNULL(I.Loc_ExchangeRate, 0),
USDAmount = (ISNULL(I.Doc_Amount, 0) * ISNULL(I.Loc_ExchangeRate, 0)) / ISNULL((
SELECT TOP 1 Rate
FROM AllExchangeRates WITH(NOLOCK)
WHERE CurrencyCode = 'USD'
AND RelationCurrencyCode = 'TRY'
AND ExchangeTypeCode = 6
AND Rate > 1
ORDER BY ABS(DATEDIFF(DAY, AllExchangeRates.Date, I.InvoiceDate)) ASC
), 1),
RN = ROW_NUMBER() OVER (
PARTITION BY I.InvoiceLineID
ORDER BY I.InvoiceDate DESC, I.InvoiceNumber
)
FROM AllInvoicesWithAttributes I WITH(NOLOCK)
LEFT OUTER JOIN CustomerAttributesFilter CAF WITH(NOLOCK)
ON CAF.CurrAccTypeCode = I.CurrAccTypeCode
AND CAF.CurrAccCode = I.CurrAccCode
WHERE I.InvoiceDate >= @p1
AND I.InvoiceDate < DATEADD(DAY, 1, @p2)
AND I.ItemTypeCode = 1
AND (I.ItemCode LIKE 'S%' OR I.ItemCode LIKE 'O%' OR I.ItemCode LIKE 'X%' OR I.ItemCode LIKE 'N%')
AND (
(I.CompanyCode = 1 AND I.ATAtt01 IN (1,2) AND I.ProcessCode IN ('WS','R'))
OR (I.CompanyCode = 4 AND I.ProcessCode = 'R')
)
AND EXISTS (
SELECT 1
FROM trDebitHeader DH WITH(NOLOCK)
INNER JOIN trDebitLine DL WITH(NOLOCK)
ON DL.DebitHeaderID = DH.DebitHeaderID
WHERE DH.ApplicationCode = 'Invoi'
AND DH.ApplicationID = I.InvoiceHeaderID
)
)
SELECT
SalesDate,
ProductCode,
ColorCode,
YakaKodu,
ItemDescription = MAX(ItemDescription),
Kategori = MAX(Kategori),
Seri = MAX(Kategori),
YasGrubu = MAX(UrunIlkGrubu),
AskiliYan = MAX(AskiliYan),
UrunIlkGrubu = MAX(UrunIlkGrubu),
UrunAnaGrubu = MAX(UrunAnaGrubu),
UrunAltGrubu = MAX(UrunAltGrubu),
MarketKey = CONCAT(MAX(Kategori), '|', MAX(UrunIlkGrubu), '|', MAX(AskiliYan), '|', MAX(UrunAnaGrubu), '|', MAX(UrunAltGrubu), '|', ChannelCode),
ChannelCode,
CustomerCountry = ISNULL(NULLIF(CustomerCountry, ''), '-'),
CustomerSegment = ISNULL(NULLIF(CustomerSegment, ''), '-'),
CustomerCode = ISNULL(NULLIF(CustomerCode, ''), '-'),
CustomerDescription = MAX(ISNULL(NULLIF(CustomerDescription, ''), '-')),
SalesQty = ISNULL(SUM(Qty1), 0),
SalesTL = ISNULL(SUM(TLAmount), 0),
SalesUSD = ISNULL(SUM(USDAmount), 0),
AvgPriceUSD = ISNULL(SUM(USDAmount) / NULLIF(SUM(Qty1), 0), 0),
InvoiceLineCount = COUNT(DISTINCT InvoiceLineID),
InvoiceCount = COUNT(DISTINCT InvoiceHeaderID),
CustomerCount = COUNT(DISTINCT CustomerCode),
LastRefNumber = MAX(InvoiceNumber)
FROM SalesLines
WHERE RN = 1
GROUP BY SalesDate, ProductCode, ColorCode, YakaKodu, ChannelCode, CustomerCountry, CustomerSegment, CustomerCode
`
}
func productPerformanceStockSQL() string {
return `
;WITH ActiveWarehouses AS (
SELECT WarehouseCode
FROM (VALUES
('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')
) W(WarehouseCode)
),
Raw AS (
SELECT
ProductCode = LTRIM(RTRIM(S.ItemCode)),
ColorCode = LTRIM(RTRIM(ISNULL(S.ColorCode, ''))),
YakaKodu = LTRIM(RTRIM(ISNULL(S.ItemDim2Code, ''))),
MovementDate = CAST(S.DocumentDate AS date),
ProcessCode = CASE
WHEN LTRIM(RTRIM(ISNULL(S.ProcessCode, ''))) <> '' THEN LTRIM(RTRIM(S.ProcessCode))
ELSE LTRIM(RTRIM(ISNULL(S.InnerProcessCode, '')))
END,
InQty = SUM(S.In_Qty1),
OutQty = SUM(S.Out_Qty1),
NetQty = SUM(S.InventoryQty1)
FROM StockWithCost S WITH(NOLOCK)
INNER JOIN ActiveWarehouses W
ON W.WarehouseCode = LTRIM(RTRIM(S.WarehouseCode))
WHERE S.ItemTypeCode = 1
AND (S.ItemCode LIKE 'S%' OR S.ItemCode LIKE 'O%' OR S.ItemCode LIKE 'X%' OR S.ItemCode LIKE 'N%')
AND S.DocumentDate < DATEADD(DAY, 1, @p2)
GROUP BY
S.ItemCode,
S.ColorCode,
S.ItemDim2Code,
CAST(S.DocumentDate AS date),
CASE
WHEN LTRIM(RTRIM(ISNULL(S.ProcessCode, ''))) <> '' THEN LTRIM(RTRIM(S.ProcessCode))
ELSE LTRIM(RTRIM(ISNULL(S.InnerProcessCode, '')))
END
),
Opening AS (
SELECT
StockDate = @p1,
ProductCode,
ColorCode,
YakaKodu,
NetQty = SUM(NetQty),
InQty = CAST(0 AS decimal(18,4)),
OutQty = CAST(0 AS decimal(18,4)),
KpiInQty = CAST(0 AS decimal(18,4)),
KpiOutQty = CAST(0 AS decimal(18,4)),
SalesMovementQty = CAST(0 AS decimal(18,4)),
ProductionInQty = CAST(0 AS decimal(18,4)),
PurchaseInQty = CAST(0 AS decimal(18,4)),
ConsumptionOutQty = CAST(0 AS decimal(18,4)),
CountDiffQty = CAST(0 AS decimal(18,4))
FROM Raw
WHERE MovementDate < @p1
GROUP BY ProductCode, ColorCode, YakaKodu
),
Daily AS (
SELECT
StockDate = MovementDate,
ProductCode,
ColorCode,
YakaKodu,
NetQty = SUM(NetQty),
InQty = SUM(InQty),
OutQty = SUM(OutQty),
KpiInQty = SUM(CASE WHEN ProcessCode IN ('OP','BP','CI') THEN InQty ELSE 0 END),
KpiOutQty = SUM(CASE WHEN ProcessCode IN ('R','WS','OC','CO') THEN OutQty ELSE 0 END),
SalesMovementQty = SUM(CASE WHEN ProcessCode IN ('R','WS') THEN OutQty ELSE 0 END),
ProductionInQty = SUM(CASE WHEN ProcessCode = 'OP' THEN InQty ELSE 0 END),
PurchaseInQty = SUM(CASE WHEN ProcessCode = 'BP' THEN InQty ELSE 0 END),
ConsumptionOutQty = SUM(CASE WHEN ProcessCode = 'OC' THEN OutQty ELSE 0 END),
CountDiffQty = SUM(CASE WHEN ProcessCode IN ('CO','CI') THEN NetQty ELSE 0 END)
FROM Raw
WHERE MovementDate BETWEEN @p1 AND @p2
GROUP BY MovementDate, ProductCode, ColorCode, YakaKodu
),
Series AS (
SELECT * FROM Opening
UNION ALL
SELECT * FROM Daily
),
Running AS (
SELECT
StockDate,
ProductCode,
ColorCode,
YakaKodu,
StockQty = SUM(NetQty) OVER (
PARTITION BY ProductCode, ColorCode, YakaKodu
ORDER BY StockDate
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
),
InQty,
OutQty,
KpiInQty,
KpiOutQty,
SalesMovementQty,
ProductionInQty,
PurchaseInQty,
ConsumptionOutQty,
CountDiffQty
FROM Series
)
SELECT
StockDate,
ProductCode,
ColorCode,
YakaKodu,
StockQty,
InQty,
OutQty,
KpiInQty,
KpiOutQty,
SalesMovementQty,
ProductionInQty,
PurchaseInQty,
ConsumptionOutQty,
CountDiffQty
FROM Running
WHERE StockDate BETWEEN @p1 AND @p2
ORDER BY StockDate, ProductCode, ColorCode, YakaKodu
`
}
func productPerformancePriceSQL() string {
return `
;WITH ProductCodes AS (
SELECT DISTINCT ItemCode
FROM trPriceListLine WITH(NOLOCK)
WHERE ItemTypeCode = 1
AND (ItemCode LIKE 'S%' OR ItemCode LIKE 'O%' OR ItemCode LIKE 'X%' OR ItemCode LIKE 'N%')
UNION
SELECT DISTINCT ItemCode
FROM prItemBasePrice WITH(NOLOCK)
WHERE ItemTypeCode = 1
AND (ItemCode LIKE 'S%' OR ItemCode LIKE 'O%' OR ItemCode LIKE 'X%' OR ItemCode LIKE 'N%')
),
LatestPrice AS (
SELECT
p.ItemCode,
DocCurrencyCode = LTRIM(RTRIM(p.DocCurrencyCode)),
p.Price,
rn = ROW_NUMBER() OVER (
PARTITION BY p.ItemCode, LTRIM(RTRIM(p.DocCurrencyCode))
ORDER BY p.ValidDate DESC, p.ValidTime DESC, p.LastUpdatedDate DESC
)
FROM trPriceListLine p WITH(NOLOCK)
INNER JOIN ProductCodes pc ON pc.ItemCode = p.ItemCode
WHERE p.ItemTypeCode = 1
AND ISNULL(p.IsDisabled, 0) = 0
AND LTRIM(RTRIM(p.DocCurrencyCode)) IN ('USD', 'TRY')
AND (
(LTRIM(RTRIM(p.DocCurrencyCode)) = 'USD' AND LTRIM(RTRIM(p.PriceGroupCode)) = 'TM-USD')
OR (LTRIM(RTRIM(p.DocCurrencyCode)) = 'TRY' AND LTRIM(RTRIM(p.PriceGroupCode)) = 'TM-TRY')
)
AND p.Price > 0
),
BasePrice AS (
SELECT
ItemCode,
BasePriceUsd = MAX(CASE WHEN DocCurrencyCode = 'USD' THEN Price END),
BasePriceTry = MAX(CASE WHEN DocCurrencyCode = 'TRY' THEN Price END)
FROM LatestPrice
WHERE rn = 1
GROUP BY ItemCode
),
Cost AS (
SELECT
b.ItemCode,
CostPriceUsd = CAST(b.Price AS decimal(18,2)),
LastPricingDate = CAST(b.PriceDate AS date),
rn = ROW_NUMBER() OVER (
PARTITION BY b.ItemCode
ORDER BY b.PriceDate DESC, b.LastUpdatedDate DESC
)
FROM prItemBasePrice b WITH(NOLOCK)
INNER JOIN ProductCodes pc ON pc.ItemCode = b.ItemCode
WHERE b.ItemTypeCode = 1
AND b.BasePriceCode = 1
AND LTRIM(RTRIM(b.CurrencyCode)) = 'USD'
)
SELECT
pc.ItemCode,
CostPriceUsd = ISNULL(c.CostPriceUsd, 0),
BasePriceUsd = ISNULL(bp.BasePriceUsd, 0),
BasePriceTry = ISNULL(bp.BasePriceTry, 0),
c.LastPricingDate
FROM ProductCodes pc
LEFT JOIN BasePrice bp ON bp.ItemCode = pc.ItemCode
LEFT JOIN Cost c ON c.ItemCode = pc.ItemCode AND c.rn = 1
`
}
func productPerformanceKPISQL() string {
return `
WITH LatestStock AS (
SELECT DISTINCT ON (product_code, color_code, yaka_kodu)
product_code, color_code, yaka_kodu, stock_qty
FROM mk_product_performance_stock_daily
WHERE stock_date <= $1
ORDER BY product_code, color_code, yaka_kodu, stock_date DESC
),
SalesAgg AS (
SELECT
product_code,
color_code,
yaka_kodu,
market_key,
MAX(item_description) AS item_description,
MAX(kategori) AS kategori,
MAX(seri) AS seri,
MAX(yas_grubu) AS yas_grubu,
MAX(askili_yan) AS askili_yan,
MAX(urun_ilk_grubu) AS urun_ilk_grubu,
MAX(urun_ana_grubu) AS urun_ana_grubu,
MAX(urun_alt_grubu) AS urun_alt_grubu,
SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '29 days') AS sales_qty_30d,
SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '89 days') AS sales_qty_90d,
SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '179 days') AS sales_qty_180d,
SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '364 days') AS sales_qty_365d,
SUM(sales_qty) FILTER (WHERE sales_date >= $1::date - INTERVAL '729 days') AS sales_qty_730d,
SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '29 days') AS sales_usd_30d,
SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '89 days') AS sales_usd_90d,
SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '179 days') AS sales_usd_180d,
SUM(sales_usd) FILTER (WHERE sales_date >= $1::date - INTERVAL '364 days') AS sales_usd_365d,
COUNT(DISTINCT NULLIF(customer_code, '-')) FILTER (WHERE sales_date >= $1::date - INTERVAL '89 days') AS customer_count_90d,
MAX(sales_date) AS last_sale_date,
MAX(last_ref_number) AS last_ref_number
FROM mk_product_performance_sales_daily
WHERE sales_date >= $1::date - INTERVAL '729 days'
AND sales_date <= $1::date
GROUP BY product_code, color_code, yaka_kodu, market_key
),
Scope AS (
SELECT
product_code, color_code, yaka_kodu, market_key,
item_description, kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu,
sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d,
sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d,
customer_count_90d, last_sale_date, last_ref_number
FROM SalesAgg
UNION ALL
SELECT
ls.product_code,
ls.color_code,
ls.yaka_kodu,
'STOK' AS market_key,
'' AS item_description,
'' AS kategori,
'' AS seri,
'' AS yas_grubu,
'' AS askili_yan,
'' AS urun_ilk_grubu,
'' AS urun_ana_grubu,
'' AS urun_alt_grubu,
0 AS sales_qty_30d,
0 AS sales_qty_90d,
0 AS sales_qty_180d,
0 AS sales_qty_365d,
0 AS sales_qty_730d,
0 AS sales_usd_30d,
0 AS sales_usd_90d,
0 AS sales_usd_180d,
0 AS sales_usd_365d,
0 AS customer_count_90d,
NULL::date AS last_sale_date,
'' AS last_ref_number
FROM LatestStock ls
WHERE COALESCE(ls.stock_qty, 0) <> 0
AND NOT EXISTS (
SELECT 1
FROM SalesAgg sa
WHERE sa.product_code = ls.product_code
AND sa.color_code = ls.color_code
AND sa.yaka_kodu = ls.yaka_kodu
)
),
Base AS (
SELECT
s.*,
COALESCE(ls.stock_qty, 0) AS stock_qty,
COALESCE(pd.cost_price_usd, 0) AS cost_price_usd,
COALESCE(pd.base_price_usd, 0) AS base_price_usd,
COALESCE(pd.base_price_try, 0) AS base_price_try,
COALESCE(s.sales_qty_90d, 0) / 90.0 AS avg_daily_sales_90d,
COALESCE(s.sales_qty_180d, 0) / 180.0 AS avg_daily_sales_180d,
CASE WHEN COALESCE(s.sales_qty_90d, 0) = 0 THEN 0 ELSE COALESCE(s.sales_usd_90d, 0) / NULLIF(s.sales_qty_90d, 0) END AS avg_price_usd_90d,
CASE WHEN COALESCE(s.sales_qty_180d, 0) = 0 THEN 0 ELSE COALESCE(s.sales_usd_180d, 0) / NULLIF(s.sales_qty_180d, 0) END AS avg_price_usd_180d,
COALESCE(s.sales_usd_90d, 0) - (COALESCE(s.sales_qty_90d, 0) * COALESCE(pd.cost_price_usd, 0)) AS gross_profit_usd_90d,
COALESCE(s.sales_usd_180d, 0) - (COALESCE(s.sales_qty_180d, 0) * COALESCE(pd.cost_price_usd, 0)) AS gross_profit_usd_180d,
CASE WHEN COALESCE(s.sales_usd_90d, 0) = 0 THEN 0
ELSE (COALESCE(s.sales_usd_90d, 0) - (COALESCE(s.sales_qty_90d, 0) * COALESCE(pd.cost_price_usd, 0))) / NULLIF(s.sales_usd_90d, 0)
END AS gross_margin_90d,
CASE WHEN COALESCE(s.sales_usd_180d, 0) = 0 THEN 0
ELSE (COALESCE(s.sales_usd_180d, 0) - (COALESCE(s.sales_qty_180d, 0) * COALESCE(pd.cost_price_usd, 0))) / NULLIF(s.sales_usd_180d, 0)
END AS gross_margin_180d,
CASE WHEN COALESCE(s.sales_qty_90d, 0) = 0 THEN 0 ELSE (COALESCE(s.sales_usd_90d, 0) / NULLIF(s.sales_qty_90d, 0)) - COALESCE(pd.cost_price_usd, 0) END AS unit_profit_cost_90d,
CASE WHEN COALESCE(s.sales_qty_180d, 0) = 0 THEN 0 ELSE (COALESCE(s.sales_usd_180d, 0) / NULLIF(s.sales_qty_180d, 0)) - COALESCE(pd.cost_price_usd, 0) END AS unit_profit_cost_180d,
CASE WHEN COALESCE(s.sales_qty_90d, 0) = 0 THEN 0 ELSE (COALESCE(s.sales_usd_90d, 0) / NULLIF(s.sales_qty_90d, 0)) - COALESCE(pd.base_price_usd, 0) END AS unit_profit_base_90d,
CASE WHEN COALESCE(s.sales_qty_180d, 0) = 0 THEN 0 ELSE (COALESCE(s.sales_usd_180d, 0) / NULLIF(s.sales_qty_180d, 0)) - COALESCE(pd.base_price_usd, 0) END AS unit_profit_base_180d
FROM Scope s
LEFT JOIN LatestStock ls ON ls.product_code=s.product_code AND ls.color_code=s.color_code AND ls.yaka_kodu=s.yaka_kodu
LEFT JOIN mk_product_performance_price_dim pd ON pd.product_code=s.product_code
),
Spread AS (
SELECT
product_code,
color_code,
yaka_kodu,
COUNT(DISTINCT market_key) AS market_count_90d
FROM SalesAgg
WHERE COALESCE(sales_qty_90d, 0) > 0
GROUP BY product_code, color_code, yaka_kodu
),
Market AS (
SELECT
market_key,
AVG(NULLIF(sales_qty_90d, 0)) AS market_avg_sales_qty_90d,
AVG(NULLIF(avg_price_usd_90d, 0)) AS market_avg_price_usd_90d,
AVG(NULLIF(gross_margin_90d, 0)) AS market_avg_margin_90d
FROM Base
GROUP BY market_key
),
Scored AS (
SELECT
b.*,
COALESCE(sp.market_count_90d, 0) AS market_count_90d,
CASE WHEN b.avg_daily_sales_90d = 0 THEN 0 ELSE b.stock_qty / NULLIF(b.avg_daily_sales_90d, 0) END AS stock_days_90d,
CASE WHEN b.avg_daily_sales_180d = 0 THEN 0 ELSE b.stock_qty / NULLIF(b.avg_daily_sales_180d, 0) END AS stock_days_180d,
CASE WHEN COALESCE(m.market_avg_sales_qty_90d, 0) = 0 THEN 0 ELSE b.sales_qty_90d / NULLIF(m.market_avg_sales_qty_90d, 0) END AS sales_index_90d,
CASE WHEN COALESCE(m.market_avg_price_usd_90d, 0) = 0 THEN 0 ELSE b.avg_price_usd_90d / NULLIF(m.market_avg_price_usd_90d, 0) END AS price_index_90d,
CASE WHEN COALESCE(m.market_avg_margin_90d, 0) = 0 THEN 0 ELSE b.gross_margin_90d / NULLIF(m.market_avg_margin_90d, 0) END AS margin_index_90d
FROM Base b
LEFT JOIN Market m ON m.market_key=b.market_key
LEFT JOIN Spread sp ON sp.product_code=b.product_code AND sp.color_code=b.color_code AND sp.yaka_kodu=b.yaka_kodu
)
INSERT INTO mk_product_performance_kpi_daily (
kpi_date, product_code, color_code, yaka_kodu, item_description,
kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, market_key, stock_qty,
sales_qty_30d, sales_qty_90d, sales_qty_180d, sales_qty_365d, sales_qty_730d,
sales_usd_30d, sales_usd_90d, sales_usd_180d, sales_usd_365d, avg_daily_sales_90d, avg_daily_sales_180d,
stock_days_90d, stock_days_180d, avg_price_usd_90d, avg_price_usd_180d, cost_price_usd, base_price_usd, base_price_try,
gross_profit_usd_90d, gross_profit_usd_180d, gross_margin_90d, gross_margin_180d,
unit_profit_cost_90d, unit_profit_cost_180d, unit_profit_base_90d, unit_profit_base_180d, market_count_90d, customer_count_90d,
sales_index_90d, price_index_90d, margin_index_90d, performance_score,
performance_bucket, recommendation, last_sale_date, last_ref_number, updated_at
)
SELECT
$1::date,
product_code, color_code, yaka_kodu, item_description,
kategori, seri, yas_grubu, askili_yan, urun_ilk_grubu, urun_ana_grubu, urun_alt_grubu, market_key, stock_qty,
COALESCE(sales_qty_30d, 0), COALESCE(sales_qty_90d, 0), COALESCE(sales_qty_180d, 0), COALESCE(sales_qty_365d, 0), COALESCE(sales_qty_730d, 0),
COALESCE(sales_usd_30d, 0), COALESCE(sales_usd_90d, 0), COALESCE(sales_usd_180d, 0), COALESCE(sales_usd_365d, 0), COALESCE(avg_daily_sales_90d, 0), COALESCE(avg_daily_sales_180d, 0),
COALESCE(stock_days_90d, 0), COALESCE(stock_days_180d, 0), COALESCE(avg_price_usd_90d, 0), COALESCE(avg_price_usd_180d, 0), COALESCE(cost_price_usd, 0), COALESCE(base_price_usd, 0), COALESCE(base_price_try, 0),
COALESCE(gross_profit_usd_90d, 0), COALESCE(gross_profit_usd_180d, 0), COALESCE(gross_margin_90d, 0), COALESCE(gross_margin_180d, 0),
COALESCE(unit_profit_cost_90d, 0), COALESCE(unit_profit_cost_180d, 0), COALESCE(unit_profit_base_90d, 0), COALESCE(unit_profit_base_180d, 0), COALESCE(market_count_90d, 0), COALESCE(customer_count_90d, 0),
COALESCE(sales_index_90d, 0), COALESCE(price_index_90d, 0), COALESCE(margin_index_90d, 0),
ROUND(
LEAST(35, COALESCE(sales_index_90d, 0) * 18)
+ LEAST(25, GREATEST(COALESCE(gross_margin_90d, 0), 0) * 50)
+ LEAST(20, COALESCE(customer_count_90d, 0) * 2)
+ LEAST(10, COALESCE(market_count_90d, 0) * 3)
+ CASE WHEN COALESCE(stock_days_90d, 0) BETWEEN 10 AND 90 THEN 10 WHEN COALESCE(stock_days_90d, 0) > 180 THEN -10 ELSE 0 END
, 4) AS performance_score,
CASE
WHEN COALESCE(sales_index_90d,0) >= 1.25 AND COALESCE(gross_margin_90d,0) >= 0.25 THEN 'YILDIZ_URUN'
WHEN COALESCE(stock_qty,0) <= 0 AND COALESCE(sales_qty_90d,0) > 0 THEN 'STOKSUZ_TALEP'
WHEN COALESCE(stock_days_90d,0) > 180 AND COALESCE(sales_index_90d,0) < 0.75 THEN 'STOK_RISKI'
WHEN COALESCE(price_index_90d,0) > 1.10 AND COALESCE(sales_index_90d,0) < 0.80 THEN 'FIYAT_BASKISI'
WHEN COALESCE(price_index_90d,0) < 0.90 AND COALESCE(sales_index_90d,0) > 1.00 THEN 'FIYAT_FIRSATI'
ELSE 'TAKIP'
END AS performance_bucket,
CASE
WHEN COALESCE(sales_index_90d,0) >= 1.25 AND COALESCE(gross_margin_90d,0) >= 0.25 THEN 'Piyasa üstü satış ve iyi marj: stok ve fiyat gücü takip edilmeli.'
WHEN COALESCE(stock_qty,0) <= 0 AND COALESCE(sales_qty_90d,0) > 0 THEN 'Talep var, stok yok: üretim/satın alma planına alınmalı.'
WHEN COALESCE(stock_days_90d,0) > 180 AND COALESCE(sales_index_90d,0) < 0.75 THEN 'Stok yüksek, satış piyasa altı: kampanya veya fiyat kontrolü gerekli.'
WHEN COALESCE(price_index_90d,0) > 1.10 AND COALESCE(sales_index_90d,0) < 0.80 THEN 'Fiyat piyasa üstünde ve satış zayıf: fiyat revizyonu değerlendirilmeli.'
WHEN COALESCE(price_index_90d,0) < 0.90 AND COALESCE(sales_index_90d,0) > 1.00 THEN 'Satış güçlü, fiyat piyasa altı: taban fiyat artışı değerlendirilebilir.'
ELSE 'Düzenli takip.'
END AS recommendation,
last_sale_date,
COALESCE(last_ref_number, ''),
now()
FROM Scored
`
}