@@ -53,11 +53,9 @@ func productPerformanceStockQuery() string {
; WITH ActiveWarehouses AS (
SELECT WarehouseCode
FROM ( VALUES
( ' 1 - 0 - 12 ' ) , ( ' 1.01 .2014 ' ) , ( ' 1.02 .2005 ' ) , ( ' 1.02 .2004 ' ) , ( ' 1 - 0 - 43 ' ) ,
( ' 4.02 .2001 ' ) , ( ' 1 - 0 - 55 ' ) , ( ' 1.01 .2003 ' ) , ( ' 1 - 0 - 21 ' ) , ( ' 1 - 0 - 2 ' ) ,
( ' 1.01 .2004 ' ) , ( ' 1 - 0 - 49 ' ) , ( ' 1 - 0 - 37 ' ) , ( ' 1 - 0 - 29 ' ) , ( ' 1 - 0 - 28 ' ) ,
( ' 1 - 0 - 10 ' ) , ( ' 100 ' ) , ( ' 1.02 .2006 ' ) , ( ' 1 - 0 - 42 ' ) , ( ' 1.01 .2002 ' ) ,
( ' 1 - 0 - 52 ' )
( ' 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 (
@@ -72,8 +70,8 @@ Raw AS (
END ,
InQty = SUM ( S . In_Qty1 ) ,
OutQty = SUM ( S . Out_Qty1 ) ,
NetQty = SUM ( S . InventoryQty1 )
FROM StockWithCost S WITH ( NOLOCK )
NetQty = SUM ( S . In_Qty1 - S . Out_Qty1 )
FROM trStock S WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( S . WarehouseCode ) )
WHERE S . ItemTypeCode = 1
@@ -185,6 +183,103 @@ Running AS (
ConsumptionOutQty ,
CountDiffQty
FROM Collapsed
) ,
CurrentStock AS (
SELECT
ProductCode = LTRIM ( RTRIM ( S . ItemCode ) ) ,
ColorCode = LTRIM ( RTRIM ( ISNULL ( S . ColorCode , ' ' ) ) ) ,
YakaKodu = LTRIM ( RTRIM ( ISNULL ( S . ItemDim2Code , ' ' ) ) ) ,
InventoryQty1 = SUM ( S . In_Qty1 - S . Out_Qty1 )
FROM trStock S WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( S . WarehouseCode ) )
WHERE S . ItemTypeCode = 1
AND (
LEN ( LTRIM ( RTRIM ( S . ItemCode ) ) ) = 13
AND (
S . ItemCode LIKE ' S % '
OR S . ItemCode LIKE ' O % '
OR S . ItemCode LIKE ' N % '
OR S . ItemCode LIKE ' X % '
OR S . ItemCode LIKE ' I % '
OR S . ItemCode LIKE ' A % '
)
)
AND ( @ p3 = ' ' OR S . ItemCode LIKE @ p3 + '%' )
AND S . DocumentDate < DATEADD ( DAY , 1 , @ p2 )
GROUP BY S . ItemCode , S . ColorCode , S . ItemDim2Code
) ,
CurrentPick AS (
SELECT
ProductCode = LTRIM ( RTRIM ( P . ItemCode ) ) ,
ColorCode = LTRIM ( RTRIM ( ISNULL ( P . ColorCode , ' ' ) ) ) ,
YakaKodu = LTRIM ( RTRIM ( ISNULL ( P . ItemDim2Code , ' ' ) ) ) ,
PickingQty1 = SUM ( P . Qty1 )
FROM PickingStates P WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( P . WarehouseCode ) )
WHERE P . ItemTypeCode = 1
AND LEN ( LTRIM ( RTRIM ( P . ItemCode ) ) ) = 13
AND ( @ p3 = ' ' OR P . ItemCode LIKE @ p3 + '%' )
GROUP BY P . ItemCode , P . ColorCode , P . ItemDim2Code
) ,
CurrentReserve AS (
SELECT
ProductCode = LTRIM ( RTRIM ( R . ItemCode ) ) ,
ColorCode = LTRIM ( RTRIM ( ISNULL ( R . ColorCode , ' ' ) ) ) ,
YakaKodu = LTRIM ( RTRIM ( ISNULL ( R . ItemDim2Code , ' ' ) ) ) ,
ReserveQty1 = SUM ( R . Qty1 )
FROM ReserveStates R WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( R . WarehouseCode ) )
WHERE R . ItemTypeCode = 1
AND LEN ( LTRIM ( RTRIM ( R . ItemCode ) ) ) = 13
AND ( @ p3 = ' ' OR R . ItemCode LIKE @ p3 + '%' )
GROUP BY R . ItemCode , R . ColorCode , R . ItemDim2Code
) ,
CurrentDisp AS (
SELECT
ProductCode = LTRIM ( RTRIM ( D . ItemCode ) ) ,
ColorCode = LTRIM ( RTRIM ( ISNULL ( D . ColorCode , ' ' ) ) ) ,
YakaKodu = LTRIM ( RTRIM ( ISNULL ( D . ItemDim2Code , ' ' ) ) ) ,
DispOrderQty1 = SUM ( D . Qty1 )
FROM DispOrderStates D WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( D . WarehouseCode ) )
WHERE D . ItemTypeCode = 1
AND LEN ( LTRIM ( RTRIM ( D . ItemCode ) ) ) = 13
AND ( @ p3 = ' ' OR D . ItemCode LIKE @ p3 + '%' )
GROUP BY D . ItemCode , D . ColorCode , D . ItemDim2Code
) ,
CurrentAvailable AS (
SELECT
StockDate = @ p2 ,
S . ProductCode ,
S . ColorCode ,
S . YakaKodu ,
StockQty = CAST ( ROUND (
ISNULL ( S . InventoryQty1 , 0 )
- ISNULL ( PK . PickingQty1 , 0 )
- ISNULL ( RS . ReserveQty1 , 0 )
- ISNULL ( DP . DispOrderQty1 , 0 ) ,
2
) AS decimal ( 18 , 4 ) ) ,
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 CurrentStock S
LEFT JOIN CurrentPick PK
ON PK . ProductCode = S . ProductCode AND PK . ColorCode = S . ColorCode AND PK . YakaKodu = S . YakaKodu
LEFT JOIN CurrentReserve RS
ON RS . ProductCode = S . ProductCode AND RS . ColorCode = S . ColorCode AND RS . YakaKodu = S . YakaKodu
LEFT JOIN CurrentDisp DP
ON DP . ProductCode = S . ProductCode AND DP . ColorCode = S . ColorCode AND DP . YakaKodu = S . YakaKodu
)
SELECT
StockDate ,
@@ -201,8 +296,11 @@ SELECT
PurchaseInQty ,
ConsumptionOutQty ,
CountDiffQty
FROM Running
WHERE StockDate BETWEEN @ p1 AND @ p2
FROM (
SELECT * FROM Running WHERE StockDate BETWEEN @ p1 AND DATEADD ( DAY , - 1 , @ p2 )
UNION ALL
SELECT * FROM CurrentAvailable
) X
ORDER BY StockDate , ProductCode , ColorCode , YakaKodu
`
}
@@ -2165,6 +2263,326 @@ LIMIT $1
return out , rows . Err ( )
}
func ListProductPerformanceSalesBreakdown ( ctx context . Context , pg * sql . DB , breakdown string , limit int ) ( [ ] models . ProductPerformanceSalesBreakdownRow , error ) {
if err := EnsureProductPerformanceTables ( pg ) ; err != nil {
return nil , err
}
if limit <= 0 || limit > 1000 {
limit = 500
}
mode := strings . ToLower ( strings . TrimSpace ( breakdown ) )
fields := map [ string ] string {
"product_code" : "s.product_code" ,
"color_code" : "s.color_code" ,
"yaka_kodu" : "s.yaka_kodu" ,
"item_description" : "MAX(s.item_description)" ,
"market_key" : "s.market_key" ,
"country" : "s.customer_country" ,
"customer_segment" : "s.customer_segment" ,
"customer_code" : "s.customer_code" ,
"customer_name" : "MAX(s.customer_name)" ,
}
selected := map [ string ] bool { }
groupCols := [ ] string { }
switch mode {
case "color_yaka_market_customer" :
selected [ "color_code" ] = true
selected [ "yaka_kodu" ] = true
selected [ "market_key" ] = true
selected [ "customer_code" ] = true
selected [ "customer_name" ] = true
groupCols = [ ] string { "s.color_code" , "s.yaka_kodu" , "s.market_key" , "s.customer_code" }
case "product_country_segment_market_customer" :
selected [ "product_code" ] = true
selected [ "item_description" ] = true
selected [ "country" ] = true
selected [ "customer_segment" ] = true
selected [ "market_key" ] = true
selected [ "customer_code" ] = true
selected [ "customer_name" ] = true
groupCols = [ ] string { "s.product_code" , "s.customer_country" , "s.customer_segment" , "s.market_key" , "s.customer_code" }
case "market_customer_product" :
selected [ "market_key" ] = true
selected [ "customer_code" ] = true
selected [ "customer_name" ] = true
selected [ "product_code" ] = true
selected [ "color_code" ] = true
selected [ "yaka_kodu" ] = true
selected [ "item_description" ] = true
groupCols = [ ] string { "s.market_key" , "s.customer_code" , "s.product_code" , "s.color_code" , "s.yaka_kodu" }
case "country_segment_market_customer_product" :
selected [ "country" ] = true
selected [ "customer_segment" ] = true
selected [ "market_key" ] = true
selected [ "customer_code" ] = true
selected [ "customer_name" ] = true
selected [ "product_code" ] = true
selected [ "color_code" ] = true
selected [ "yaka_kodu" ] = true
selected [ "item_description" ] = true
groupCols = [ ] string { "s.customer_country" , "s.customer_segment" , "s.market_key" , "s.customer_code" , "s.product_code" , "s.color_code" , "s.yaka_kodu" }
default :
mode = "color_yaka_market_customer"
selected [ "color_code" ] = true
selected [ "yaka_kodu" ] = true
selected [ "market_key" ] = true
selected [ "customer_code" ] = true
selected [ "customer_name" ] = true
groupCols = [ ] string { "s.color_code" , "s.yaka_kodu" , "s.market_key" , "s.customer_code" }
}
selectExpr := func ( name string ) string {
if selected [ name ] {
return fields [ name ] + " AS " + name
}
return "''::text AS " + name
}
query := fmt . Sprintf ( `
WITH Agg AS (
SELECT
$ 2 : : text AS breakdown ,
% s ,
% s ,
% s ,
% s ,
% s ,
% s ,
% s ,
% s ,
% s ,
COUNT ( DISTINCT product_code ) AS product_count ,
COUNT ( DISTINCT customer_code ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' AND COALESCE ( customer_code , ' ' ) < > ' ' ) : : integer AS customer_count_90d ,
COALESCE ( SUM ( invoice_count ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) : : integer AS invoice_count_90d ,
COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) AS sales_qty_90d ,
COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) AS sales_usd_90d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
END AS avg_price_usd_90d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * base_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
END AS base_price_usd_90d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * cost_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 89 days ' ) , 0 )
END AS cost_price_usd_90d ,
COUNT ( DISTINCT customer_code ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' AND COALESCE ( customer_code , ' ' ) < > ' ' ) : : integer AS customer_count_180d ,
COALESCE ( SUM ( invoice_count ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) : : integer AS invoice_count_180d ,
COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) AS sales_qty_180d ,
COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) AS sales_usd_180d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
END AS avg_price_usd_180d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * base_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
END AS base_price_usd_180d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * cost_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 179 days ' ) , 0 )
END AS cost_price_usd_180d ,
COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 ) AS sales_qty_365d ,
COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 ) AS sales_usd_365d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
END AS avg_price_usd_365d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * base_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
END AS base_price_usd_365d ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * cost_price_usd ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= current_date - INTERVAL ' 359 days ' ) , 0 )
END AS cost_price_usd_365d ,
COUNT ( DISTINCT customer_code ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' AND COALESCE ( customer_code , ' ' ) < > ' ' ) : : integer AS customer_count_total ,
COALESCE ( SUM ( invoice_count ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) : : integer AS invoice_count_total ,
COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) AS sales_qty_total ,
COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) AS sales_usd_total ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_usd ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
END AS avg_price_usd_total ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * base_price_usd ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
END AS base_price_usd_total ,
CASE WHEN COALESCE ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 ) = 0 THEN 0
ELSE COALESCE ( SUM ( sales_qty * cost_price_usd ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
/ NULLIF ( SUM ( sales_qty ) FILTER ( WHERE sales_date >= DATE ' 2022 - 01 - 01 ' ) , 0 )
END AS cost_price_usd_total ,
COALESCE ( to_char ( MAX ( sales_date ) , ' YYYY - MM - DD ' ) , ' ' ) AS last_sale_date
FROM (
SELECT
s . * ,
COALESCE ( pd . base_price_usd , 0 ) AS base_price_usd ,
COALESCE ( pd . cost_price_usd , 0 ) AS cost_price_usd
FROM mk_product_performance_sales_daily s
LEFT JOIN mk_product_performance_price_dim pd ON pd . product_code = s . product_code
) s
WHERE sales_date >= DATE ' 2022 - 01 - 01 '
GROUP BY % s
) ,
Enriched AS (
SELECT
Agg . * ,
sales_usd_90d - ( sales_qty_90d * base_price_usd_90d ) AS gross_profit_base_usd_90d ,
sales_usd_90d - ( sales_qty_90d * cost_price_usd_90d ) AS gross_profit_cost_usd_90d ,
CASE WHEN sales_usd_90d <= 0 THEN 0 ELSE ( sales_usd_90d - ( sales_qty_90d * base_price_usd_90d ) ) / NULLIF ( sales_usd_90d , 0 ) END AS gross_margin_base_90d ,
CASE WHEN sales_usd_90d <= 0 THEN 0 ELSE ( sales_usd_90d - ( sales_qty_90d * cost_price_usd_90d ) ) / NULLIF ( sales_usd_90d , 0 ) END AS gross_margin_cost_90d ,
sales_usd_180d - ( sales_qty_180d * base_price_usd_180d ) AS gross_profit_base_usd_180d ,
sales_usd_180d - ( sales_qty_180d * cost_price_usd_180d ) AS gross_profit_cost_usd_180d ,
CASE WHEN sales_usd_180d <= 0 THEN 0 ELSE ( sales_usd_180d - ( sales_qty_180d * base_price_usd_180d ) ) / NULLIF ( sales_usd_180d , 0 ) END AS gross_margin_base_180d ,
CASE WHEN sales_usd_180d <= 0 THEN 0 ELSE ( sales_usd_180d - ( sales_qty_180d * cost_price_usd_180d ) ) / NULLIF ( sales_usd_180d , 0 ) END AS gross_margin_cost_180d ,
sales_usd_365d - ( sales_qty_365d * base_price_usd_365d ) AS gross_profit_base_usd_365d ,
sales_usd_365d - ( sales_qty_365d * cost_price_usd_365d ) AS gross_profit_cost_usd_365d ,
CASE WHEN sales_usd_365d <= 0 THEN 0 ELSE ( sales_usd_365d - ( sales_qty_365d * base_price_usd_365d ) ) / NULLIF ( sales_usd_365d , 0 ) END AS gross_margin_base_365d ,
CASE WHEN sales_usd_365d <= 0 THEN 0 ELSE ( sales_usd_365d - ( sales_qty_365d * cost_price_usd_365d ) ) / NULLIF ( sales_usd_365d , 0 ) END AS gross_margin_cost_365d ,
sales_usd_total - ( sales_qty_total * base_price_usd_total ) AS gross_profit_base_usd_total ,
sales_usd_total - ( sales_qty_total * cost_price_usd_total ) AS gross_profit_cost_usd_total ,
CASE WHEN sales_usd_total <= 0 THEN 0 ELSE ( sales_usd_total - ( sales_qty_total * base_price_usd_total ) ) / NULLIF ( sales_usd_total , 0 ) END AS gross_margin_base_total ,
CASE WHEN sales_usd_total <= 0 THEN 0 ELSE ( sales_usd_total - ( sales_qty_total * cost_price_usd_total ) ) / NULLIF ( sales_usd_total , 0 ) END AS gross_margin_cost_total ,
( base_price_usd_total > 0 AND cost_price_usd_total > 0 ) AS has_cost
FROM Agg
) ,
Scored AS (
SELECT
Enriched . * ,
CASE WHEN AVG ( NULLIF ( sales_usd_90d , 0 ) ) OVER ( ) IS NULL THEN 0
ELSE sales_usd_90d / NULLIF ( AVG ( NULLIF ( sales_usd_90d , 0 ) ) OVER ( ) , 0 )
END AS sales_index_90d
FROM Enriched
)
SELECT
breakdown ,
product_code ,
color_code ,
yaka_kodu ,
item_description ,
market_key ,
country ,
customer_segment ,
customer_code ,
customer_name ,
product_count ,
customer_count_90d ,
invoice_count_90d ,
sales_qty_90d ,
sales_usd_90d ,
avg_price_usd_90d ,
base_price_usd_90d ,
cost_price_usd_90d ,
gross_profit_base_usd_90d ,
gross_profit_cost_usd_90d ,
gross_margin_base_90d ,
gross_margin_cost_90d ,
customer_count_180d ,
invoice_count_180d ,
sales_qty_180d ,
sales_usd_180d ,
avg_price_usd_180d ,
base_price_usd_180d ,
cost_price_usd_180d ,
gross_profit_base_usd_180d ,
gross_profit_cost_usd_180d ,
gross_margin_base_180d ,
gross_margin_cost_180d ,
sales_qty_365d ,
sales_usd_365d ,
avg_price_usd_365d ,
base_price_usd_365d ,
cost_price_usd_365d ,
gross_profit_base_usd_365d ,
gross_profit_cost_usd_365d ,
gross_margin_base_365d ,
gross_margin_cost_365d ,
customer_count_total ,
invoice_count_total ,
sales_qty_total ,
sales_usd_total ,
avg_price_usd_total ,
base_price_usd_total ,
cost_price_usd_total ,
gross_profit_base_usd_total ,
gross_profit_cost_usd_total ,
gross_margin_base_total ,
gross_margin_cost_total ,
has_cost ,
sales_index_90d ,
CASE WHEN NOT has_cost THEN 0 ELSE
ROUND (
LEAST ( 35 , sales_index_90d * 18 )
+ LEAST ( 30 , GREATEST ( gross_margin_cost_90d , 0 ) * 60 )
+ LEAST ( 15 , invoice_count_90d * 1.5 )
+ LEAST ( 10 , sales_qty_90d / 10 )
+ CASE WHEN last_sale_date >= to_char ( current_date - INTERVAL ' 30 days ',' YYYY - MM - DD ' ) THEN 10 ELSE 0 END ,
4
)
END AS performance_score ,
CASE
WHEN NOT has_cost THEN ' MALIYET_YOK '
WHEN sales_index_90d >= 1.25 AND customer_count_90d >= 2 THEN ' YILDIZ_URUN '
WHEN sales_qty_90d = 0 AND sales_qty_365d > 0 THEN ' STOK_RISKI '
WHEN gross_margin_cost_90d < 0 THEN ' FIYAT_BASKISI '
WHEN sales_index_90d < 0.75 THEN ' TAKIP '
ELSE ' TAKIP '
END AS performance_bucket ,
CASE
WHEN sales_index_90d >= 1.25 AND customer_count_90d >= 2 THEN ' Güçlü satış kı rı lı mı '
WHEN sales_qty_90d = 0 AND sales_qty_365d > 0 THEN ' Son 90 gün zayı f , kontrol et '
ELSE ' Takip '
END AS recommendation ,
last_sale_date
FROM Scored
ORDER BY sales_usd_90d DESC , sales_qty_90d DESC
LIMIT $ 1
` ,
selectExpr ( "product_code" ) ,
selectExpr ( "color_code" ) ,
selectExpr ( "yaka_kodu" ) ,
selectExpr ( "item_description" ) ,
selectExpr ( "market_key" ) ,
selectExpr ( "country" ) ,
selectExpr ( "customer_segment" ) ,
selectExpr ( "customer_code" ) ,
selectExpr ( "customer_name" ) ,
strings . Join ( groupCols , ", " ) ,
)
rows , err := pg . QueryContext ( ctx , query , limit , mode )
if err != nil {
return nil , err
}
defer rows . Close ( )
out := make ( [ ] models . ProductPerformanceSalesBreakdownRow , 0 , limit )
for rows . Next ( ) {
var r models . ProductPerformanceSalesBreakdownRow
if err := rows . Scan (
& r . Breakdown , & r . ProductCode , & r . ColorCode , & r . YakaKodu , & r . ItemDescription ,
& r . MarketKey , & r . Country , & r . CustomerSegment , & r . CustomerCode , & r . CustomerName ,
& r . ProductCount , & r . CustomerCount90 , & r . InvoiceCount90 , & r . SalesQty90 , & r . SalesUSD90 ,
& r . AvgPriceUSD90 , & r . BasePriceUSD90 , & r . CostPriceUSD90 , & r . GrossProfitBase90 ,
& r . GrossProfitCost90 , & r . GrossMarginBase90 , & r . GrossMarginCost90 , & r . CustomerCount180 ,
& r . InvoiceCount180 , & r . SalesQty180 , & r . SalesUSD180 , & r . AvgPriceUSD180 , & r . BasePriceUSD180 ,
& r . CostPriceUSD180 , & r . GrossProfitBase180 , & r . GrossProfitCost180 , & r . GrossMarginBase180 ,
& r . GrossMarginCost180 , & r . SalesQty365 , & r . SalesUSD365 , & r . AvgPriceUSD365 , & r . BasePriceUSD365 ,
& r . CostPriceUSD365 , & r . GrossProfitBase365 , & r . GrossProfitCost365 , & r . GrossMarginBase365 ,
& r . GrossMarginCost365 , & r . CustomerCountTotal , & r . InvoiceCountTotal , & r . SalesQtyTotal , & r . SalesUSDTotal ,
& r . AvgPriceUSDTotal , & r . BasePriceUSDTotal , & r . CostPriceUSDTotal , & r . GrossProfitBaseTotal ,
& r . GrossProfitCostTotal , & r . GrossMarginBaseTotal , & r . GrossMarginCostTotal , & r . HasCost ,
& r . SalesIndex90 , & r . PerformanceScore ,
& r . PerformanceBucket , & r . Recommendation , & r . LastSaleDate ,
) ; err != nil {
return nil , err
}
out = append ( out , r )
}
return out , rows . Err ( )
}
func ListProductPerformanceSalesDetails ( ctx context . Context , pg * sql . DB , productCode , colorCode , yakaKodu string , limit int ) ( [ ] models . ProductPerformanceSalesDetailRow , error ) {
if limit <= 0 || limit > 500 {
limit = 100
@@ -2211,26 +2629,83 @@ func ListProductPerformanceStockSizes(ctx context.Context, productCode, colorCod
; WITH ActiveWarehouses AS (
SELECT WarehouseCode
FROM ( VALUES
( ' 1 - 0 - 12 ' ) , ( ' 1.01 .2014 ' ) , ( ' 1.02 .2005 ' ) , ( ' 1.02 .2004 ' ) , ( ' 1 - 0 - 43 ' ) ,
( ' 4.02 .2001 ' ) , ( ' 1 - 0 - 55 ' ) , ( ' 1.01 .2003 ' ) , ( ' 1 - 0 - 21 ' ) , ( ' 1 - 0 - 2 ' ) ,
( ' 1.01 .2004 ' ) , ( ' 1 - 0 - 49 ' ) , ( ' 1 - 0 - 37 ' ) , ( ' 1 - 0 - 29 ' ) , ( ' 1 - 0 - 28 ' ) ,
( ' 1 - 0 - 10 ' ) , ( ' 100 ' ) , ( ' 1.02 .2006 ' ) , ( ' 1 - 0 - 42 ' ) , ( ' 1.01 .2002 ' ) ,
( ' 1 - 0 - 52 ' )
( ' 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 )
) ,
Stock AS (
SELECT
SizeCode = LTRIM ( RTRIM ( ISNULL ( S . ItemDim1Code , ' ' ) ) ) ,
InventoryQty1 = SUM ( S . In_Qty1 - S . Out_Qty1 )
FROM trStock S WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( S . WarehouseCode ) )
WHERE S . ItemTypeCode = 1
AND LTRIM ( RTRIM ( S . ItemCode ) ) = @ p1
AND LTRIM ( RTRIM ( ISNULL ( S . ColorCode , ' ' ) ) ) = @ p2
AND LTRIM ( RTRIM ( ISNULL ( S . ItemDim2Code , ' ' ) ) ) = @ p3
GROUP BY LTRIM ( RTRIM ( ISNULL ( S . ItemDim1Code , ' ' ) ) )
) ,
Pick AS (
SELECT
SizeCode = LTRIM ( RTRIM ( ISNULL ( P . ItemDim1Code , ' ' ) ) ) ,
PickingQty1 = SUM ( P . Qty1 )
FROM PickingStates P WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( P . WarehouseCode ) )
WHERE P . ItemTypeCode = 1
AND LTRIM ( RTRIM ( P . ItemCode ) ) = @ p1
AND LTRIM ( RTRIM ( ISNULL ( P . ColorCode , ' ' ) ) ) = @ p2
AND LTRIM ( RTRIM ( ISNULL ( P . ItemDim2Code , ' ' ) ) ) = @ p3
GROUP BY LTRIM ( RTRIM ( ISNULL ( P . ItemDim1Code , ' ' ) ) )
) ,
Reserve AS (
SELECT
SizeCode = LTRIM ( RTRIM ( ISNULL ( R . ItemDim1Code , ' ' ) ) ) ,
ReserveQty1 = SUM ( R . Qty1 )
FROM ReserveStates R WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( R . WarehouseCode ) )
WHERE R . ItemTypeCode = 1
AND LTRIM ( RTRIM ( R . ItemCode ) ) = @ p1
AND LTRIM ( RTRIM ( ISNULL ( R . ColorCode , ' ' ) ) ) = @ p2
AND LTRIM ( RTRIM ( ISNULL ( R . ItemDim2Code , ' ' ) ) ) = @ p3
GROUP BY LTRIM ( RTRIM ( ISNULL ( R . ItemDim1Code , ' ' ) ) )
) ,
Disp AS (
SELECT
SizeCode = LTRIM ( RTRIM ( ISNULL ( D . ItemDim1Code , ' ' ) ) ) ,
DispOrderQty1 = SUM ( D . Qty1 )
FROM DispOrderStates D WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( D . WarehouseCode ) )
WHERE D . ItemTypeCode = 1
AND LTRIM ( RTRIM ( D . ItemCode ) ) = @ p1
AND LTRIM ( RTRIM ( ISNULL ( D . ColorCode , ' ' ) ) ) = @ p2
AND LTRIM ( RTRIM ( ISNULL ( D . ItemDim2Code , ' ' ) ) ) = @ p3
GROUP BY LTRIM ( RTRIM ( ISNULL ( D . ItemDim1Code , ' ' ) ) )
)
SELECT
SizeCode = LTRIM ( RTRIM ( ISNULL ( S . ItemDim1Code , ' ' ) ) ) ,
StockQty = SUM ( S . InventoryQty1 )
FROM StockWithCost S WITH ( NOLOCK )
INNER JOIN ActiveWarehouses W
ON W . WarehouseCode = LTRIM ( RTRIM ( S . WarehouseCode ) )
WHERE S . ItemTypeCode = 1
AND LTRIM ( RTRIM ( S . ItemCode ) ) = @ p1
AND LTRIM ( RTRIM ( ISNULL ( S . ColorCode , ' ' ) ) ) = @ p2
AND LTRIM ( RTRIM ( ISNULL ( S . ItemDim2Code , ' ' ) ) ) = @ p3
GROUP BY LTRIM ( RTRIM ( ISNULL ( S . ItemDim1Code , ' ' ) ) )
HAVING SUM ( S . InventoryQty1 ) < > 0
ORDER BY LTRIM ( RTRIM ( ISNULL ( S . ItemDim1Code , ' ' ) ) )
S . SizeCode ,
StockQty = CAST ( ROUND (
ISNULL ( S . InventoryQty1 , 0 )
- ISNULL ( P . PickingQty1 , 0 )
- ISNULL ( R . ReserveQty1 , 0 )
- ISNULL ( D . DispOrderQty1 , 0 ) ,
2
) AS FLOAT )
FROM Stock S
LEFT JOIN Pick P ON P . SizeCode = S . SizeCode
LEFT JOIN Reserve R ON R . SizeCode = S . SizeCode
LEFT JOIN Disp D ON D . SizeCode = S . SizeCode
WHERE (
ISNULL ( S . InventoryQty1 , 0 )
- ISNULL ( P . PickingQty1 , 0 )
- ISNULL ( R . ReserveQty1 , 0 )
- ISNULL ( D . DispOrderQty1 , 0 )
) < > 0
ORDER BY S . SizeCode
` , strings . TrimSpace ( productCode ) , strings . TrimSpace ( colorCode ) , strings . TrimSpace ( yakaKodu ) )
if err != nil {
return nil , err