ui: update ProductPerformanceProfitability page to enhance table views and tabs management

This commit is contained in:
M_Kececi
2026-07-02 23:08:02 +03:00
parent f3d4661cff
commit d419f52e3c
6 changed files with 1373 additions and 134 deletions
+501 -26
View File
@@ -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