Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-06-04 14:33:10 +03:00
parent 00626152c2
commit 7b1588d69d
11 changed files with 2065 additions and 78 deletions

View File

@@ -36,6 +36,583 @@ type ProductPricingPage struct {
Limit int
}
func GetAllProductPricingRows(ctx context.Context, chunkSize int, filters ProductPricingFilters, sortBy string, descending bool) ([]models.ProductPricing, error) {
if chunkSize <= 0 || chunkSize > 1000 {
chunkSize = 1000
}
paramIndex := 1
args := make([]any, 0, 64)
nextParam := func() string {
name := "@p" + strconv.Itoa(paramIndex)
paramIndex++
return name
}
whereParts := []string{
"ProductAtt42 IN ('SERI', 'AKSESUAR')",
"IsBlocked = 0",
"LEN(LTRIM(RTRIM(ProductCode))) = 13",
}
addInFilter := func(expr string, values []string) {
clean := make([]string, 0, len(values))
for _, v := range values {
v = strings.TrimSpace(v)
if v == "" {
continue
}
clean = append(clean, v)
}
if len(clean) == 0 {
return
}
ors := make([]string, 0, len(clean))
for _, v := range clean {
p := nextParam()
ors = append(ors, expr+" = "+p)
args = append(args, v)
}
whereParts = append(whereParts, "("+strings.Join(ors, " OR ")+")")
}
brandGroupExpr := `CASE ABS(CHECKSUM(LTRIM(RTRIM(ProductCode)))) % 3
WHEN 0 THEN 'MARKA GRUBU A'
WHEN 1 THEN 'MARKA GRUBU B'
ELSE 'MARKA GRUBU C'
END`
addInFilter("LTRIM(RTRIM(ProductCode))", filters.ProductCode)
addInFilter(brandGroupExpr, filters.BrandGroup)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt45Desc)), '')", filters.AskiliYan)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt44Desc)), '')", filters.Kategori)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '')", filters.UrunIlkGrubu)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt01Desc)), '')", filters.UrunAnaGrubu)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt02Desc)), '')", filters.UrunAltGrubu)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt41Desc)), '')", filters.Icerik)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt29Desc)), '')", filters.Karisim)
addInFilter("COALESCE(LTRIM(RTRIM(ProductAtt10Desc)), '')", filters.Marka)
if q := strings.TrimSpace(filters.Search); q != "" {
p := nextParam()
args = append(args, "%"+q+"%")
whereParts = append(whereParts, "("+strings.Join([]string{
"LTRIM(RTRIM(ProductCode)) LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt45Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt44Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt42Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt01Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt02Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt41Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt29Desc)), '') LIKE " + p,
"COALESCE(LTRIM(RTRIM(ProductAtt10Desc)), '') LIKE " + p,
}, " OR ")+")")
}
whereSQL := strings.Join(whereParts, " AND ")
sortBy = strings.TrimSpace(sortBy)
orderDir := "DESC"
if !descending {
orderDir = "ASC"
}
orderExpr := "CAST(ROUND(ISNULL(sb.InventoryQty1, 0) - ISNULL(pb.PickingQty1, 0) - ISNULL(rb.ReserveQty1, 0) - ISNULL(db.DispOrderQty1, 0), 2) AS DECIMAL(18, 2))"
if sortBy == "productCode" {
orderExpr = "rc.ProductCode"
orderDir = "ASC"
}
baseQuery := `
IF OBJECT_ID('tempdb..#req_codes') IS NOT NULL DROP TABLE #req_codes;
IF OBJECT_ID('tempdb..#stock_base') IS NOT NULL DROP TABLE #stock_base;
IF OBJECT_ID('tempdb..#pick_base') IS NOT NULL DROP TABLE #pick_base;
IF OBJECT_ID('tempdb..#reserve_base') IS NOT NULL DROP TABLE #reserve_base;
IF OBJECT_ID('tempdb..#disp_base') IS NOT NULL DROP TABLE #disp_base;
SELECT
f.ProductCode,
MAX(f.BrandGroupSec) AS BrandGroupSec,
MAX(f.AskiliYan) AS AskiliYan,
MAX(f.Kategori) AS Kategori,
MAX(f.UrunIlkGrubu) AS UrunIlkGrubu,
MAX(f.UrunAnaGrubu) AS UrunAnaGrubu,
MAX(f.UrunAltGrubu) AS UrunAltGrubu,
MAX(f.Icerik) AS Icerik,
MAX(f.Karisim) AS Karisim,
MAX(f.Marka) AS Marka,
MAX(f.BrandCode) AS BrandCode
INTO #req_codes
FROM (
SELECT
LTRIM(RTRIM(ProductCode)) AS ProductCode,
` + brandGroupExpr + ` AS BrandGroupSec,
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,
COALESCE(LTRIM(RTRIM(ProductAtt10)), '') AS BrandCode
FROM ProductFilterWithDescription('TR')
WHERE ` + whereSQL + `
) f
GROUP BY f.ProductCode;
CREATE CLUSTERED INDEX IX_req_codes_ProductCode ON #req_codes(ProductCode);
SELECT
LTRIM(RTRIM(s.ItemCode)) AS ItemCode,
SUM(s.In_Qty1 - s.Out_Qty1) AS InventoryQty1
INTO #stock_base
FROM trStock s WITH(NOLOCK)
INNER JOIN #req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(s.ItemCode))
WHERE s.ItemTypeCode = 1
AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13
GROUP BY LTRIM(RTRIM(s.ItemCode));
CREATE CLUSTERED INDEX IX_stock_base_ItemCode ON #stock_base(ItemCode);
SELECT
LTRIM(RTRIM(p.ItemCode)) AS ItemCode,
SUM(p.Qty1) AS PickingQty1
INTO #pick_base
FROM PickingStates p
INNER JOIN #req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(p.ItemCode))
WHERE p.ItemTypeCode = 1
AND LEN(LTRIM(RTRIM(p.ItemCode))) = 13
GROUP BY LTRIM(RTRIM(p.ItemCode));
SELECT
LTRIM(RTRIM(r.ItemCode)) AS ItemCode,
SUM(r.Qty1) AS ReserveQty1
INTO #reserve_base
FROM ReserveStates r
INNER JOIN #req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(r.ItemCode))
WHERE r.ItemTypeCode = 1
AND LEN(LTRIM(RTRIM(r.ItemCode))) = 13
GROUP BY LTRIM(RTRIM(r.ItemCode));
SELECT
LTRIM(RTRIM(d.ItemCode)) AS ItemCode,
SUM(d.Qty1) AS DispOrderQty1
INTO #disp_base
FROM DispOrderStates d
INNER JOIN #req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(d.ItemCode))
WHERE d.ItemTypeCode = 1
AND LEN(LTRIM(RTRIM(d.ItemCode))) = 13
GROUP BY LTRIM(RTRIM(d.ItemCode));
SELECT
rc.ProductCode,
rc.BrandGroupSec,
rc.AskiliYan,
rc.Kategori,
rc.UrunIlkGrubu,
rc.UrunAnaGrubu,
rc.UrunAltGrubu,
rc.Icerik,
rc.Karisim,
rc.Marka,
rc.BrandCode,
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 #req_codes rc
LEFT JOIN #stock_base sb
ON sb.ItemCode = rc.ProductCode
LEFT JOIN #pick_base pb
ON pb.ItemCode = rc.ProductCode
LEFT JOIN #reserve_base rb
ON rb.ItemCode = rc.ProductCode
LEFT JOIN #disp_base db
ON db.ItemCode = rc.ProductCode
ORDER BY
` + orderExpr + ` ` + orderDir + `,
rc.ProductCode ASC;
`
rows, err := db.MssqlDB.QueryContext(ctx, baseQuery, args...)
if err != nil {
return nil, err
}
defer rows.Close()
out := make([]models.ProductPricing, 0, 2048)
for rows.Next() {
var item models.ProductPricing
if err := rows.Scan(
&item.ProductCode,
&item.BrandGroupSec,
&item.AskiliYan,
&item.Kategori,
&item.UrunIlkGrubu,
&item.UrunAnaGrubu,
&item.UrunAltGrubu,
&item.Icerik,
&item.Karisim,
&item.Marka,
&item.BrandCode,
&item.StockQty,
); err != nil {
return nil, err
}
out = append(out, item)
}
if err := rows.Err(); err != nil {
return nil, err
}
if len(out) == 0 {
return out, nil
}
if err := enrichAllProductPricingRows(ctx, out, chunkSize); err != nil {
return nil, err
}
return out, nil
}
func enrichAllProductPricingRows(ctx context.Context, out []models.ProductPricing, chunkSize int) error {
if len(out) == 0 {
return nil
}
if chunkSize <= 0 || chunkSize > 1000 {
chunkSize = 1000
}
indexByCode := make(map[string]int, len(out))
codes := make([]string, 0, len(out))
for i := range out {
code := strings.TrimSpace(out[i].ProductCode)
if code == "" {
continue
}
indexByCode[code] = i
codes = append(codes, code)
}
for _, chunk := range chunkStringSlice(codes, chunkSize) {
valueRows := make([]string, 0, len(chunk))
metricArgs := make([]any, 0, len(chunk))
for i, code := range chunk {
paramName := "@p" + strconv.Itoa(i+1)
valueRows = append(valueRows, "("+paramName+")")
metricArgs = append(metricArgs, code)
}
metricsQuery := `
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
),
latest_pricelist_line AS (
SELECT
LTRIM(RTRIM(p.ItemCode)) AS ItemCode,
LTRIM(RTRIM(p.DocCurrencyCode)) AS DocCurrencyCode,
CAST(p.Price AS DECIMAL(18, 2)) AS Price,
ROW_NUMBER() OVER (
PARTITION BY LTRIM(RTRIM(p.ItemCode)), LTRIM(RTRIM(p.DocCurrencyCode))
ORDER BY p.ValidDate DESC, p.ValidTime DESC, p.LastUpdatedDate DESC
) AS rn
FROM dbo.trPriceListLine p WITH(NOLOCK)
INNER JOIN req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(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 IS NOT NULL
AND p.Price > 0
),
base_prices AS (
SELECT
ItemCode,
MAX(CASE WHEN DocCurrencyCode = 'USD' THEN Price END) AS BasePriceUsd,
MAX(CASE WHEN DocCurrencyCode = 'TRY' THEN Price END) AS BasePriceTry
FROM latest_pricelist_line
WHERE rn = 1
GROUP BY ItemCode
),
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
INNER JOIN req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(b.ItemCode))
WHERE b.ItemTypeCode = 1
AND b.BasePriceCode = 1
AND LTRIM(RTRIM(b.CurrencyCode)) = 'USD'
),
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)
INNER JOIN req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(s.ItemCode))
WHERE s.ItemTypeCode = 1
AND LEN(LTRIM(RTRIM(s.ItemCode))) = 13
AND s.In_Qty1 > 0
AND LTRIM(RTRIM(s.InnerProcessCode)) = 'OP'
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'
)
GROUP BY LTRIM(RTRIM(s.ItemCode))
)
SELECT
rc.ProductCode,
COALESCE(lp.CostPrice, 0) AS CostPrice,
COALESCE(bp.BasePriceUsd, 0) AS BasePriceUsd,
COALESCE(bp.BasePriceTry, 0) AS BasePriceTry,
COALESCE(se.StockEntryDate, '') AS StockEntryDate,
COALESCE(lp.LastPricingDate, '') AS LastPricingDate
FROM req_codes rc
LEFT JOIN latest_base_price lp
ON lp.ItemCode = rc.ProductCode
AND lp.rn = 1
LEFT JOIN base_prices bp
ON bp.ItemCode = rc.ProductCode
LEFT JOIN stock_entry_dates se
ON se.ItemCode = rc.ProductCode;
`
metricRows, err := db.MssqlDB.QueryContext(ctx, metricsQuery, metricArgs...)
if err != nil {
return fmt.Errorf("metrics query failed: %w", err)
}
for metricRows.Next() {
var code string
var costPrice, basePriceUsd, basePriceTry float64
var stockEntryDate, lastPricingDate string
if err := metricRows.Scan(&code, &costPrice, &basePriceUsd, &basePriceTry, &stockEntryDate, &lastPricingDate); err != nil {
_ = metricRows.Close()
return err
}
if idx, ok := indexByCode[strings.TrimSpace(code)]; ok {
out[idx].CostPrice = costPrice
out[idx].BasePriceUsd = basePriceUsd
out[idx].BasePriceTry = basePriceTry
out[idx].StockEntryDate = stockEntryDate
out[idx].LastPricingDate = lastPricingDate
}
}
if err := metricRows.Err(); err != nil {
_ = metricRows.Close()
return err
}
_ = metricRows.Close()
if uretimDB := db.GetUretimDB(); uretimDB != nil {
costingQuery := `
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
)
SELECT
LTRIM(RTRIM(m.UrunKodu)) AS UrunKodu,
CONVERT(VARCHAR(10), MAX(m.Tarihi), 23) AS LastCostingDate
FROM dbo.spUrtOnMLMas m WITH(NOLOCK)
INNER JOIN req_codes rc
ON rc.ProductCode = LTRIM(RTRIM(m.UrunKodu))
GROUP BY LTRIM(RTRIM(m.UrunKodu));
`
costRows, err := uretimDB.QueryContext(ctx, costingQuery, metricArgs...)
if err == nil {
for costRows.Next() {
var code, d string
if err := costRows.Scan(&code, &d); err != nil {
_ = costRows.Close()
return err
}
if idx, ok := indexByCode[strings.TrimSpace(code)]; ok && strings.TrimSpace(d) != "" {
out[idx].LastCostingDate = strings.TrimSpace(d)
}
}
if err := costRows.Err(); err != nil {
_ = costRows.Close()
return err
}
_ = costRows.Close()
}
}
if pg := db.PgDB; pg != nil {
tierSQL := `
WITH ranked AS (
SELECT
mmitem.code AS code,
sdprc.sdprcgrp_id AS grp,
sdprc.crn AS crn,
COALESCE(sdprc.prc, 0) AS prc,
ROW_NUMBER() OVER (
PARTITION BY mmitem.code, sdprc.crn, sdprc.sdprcgrp_id
ORDER BY sdprc.zlins_dttm DESC
) AS rn
FROM sdprc
JOIN mmitem ON mmitem.id = sdprc.mmitem_id
WHERE mmitem.code = ANY($1)
AND sdprc.sdprcgrp_id BETWEEN 1 AND 6
AND sdprc.crn IN ('USD', 'EUR', 'TRY')
AND sdprc.prc IS NOT NULL
AND sdprc.prc > 0
)
SELECT code, grp, crn, prc
FROM ranked
WHERE rn = 1;
`
pgRows, err := pg.QueryContext(ctx, tierSQL, pq.Array(chunk))
if err == nil {
for pgRows.Next() {
var code, crn string
var grp int
var prc float64
if err := pgRows.Scan(&code, &grp, &crn, &prc); err != nil {
_ = pgRows.Close()
return err
}
idx, ok := indexByCode[strings.TrimSpace(code)]
if !ok {
continue
}
switch strings.ToUpper(strings.TrimSpace(crn)) {
case "USD":
switch grp {
case 1:
out[idx].USD1 = prc
case 2:
out[idx].USD2 = prc
case 3:
out[idx].USD3 = prc
case 4:
out[idx].USD4 = prc
case 5:
out[idx].USD5 = prc
case 6:
out[idx].USD6 = prc
}
case "EUR":
switch grp {
case 1:
out[idx].EUR1 = prc
case 2:
out[idx].EUR2 = prc
case 3:
out[idx].EUR3 = prc
case 4:
out[idx].EUR4 = prc
case 5:
out[idx].EUR5 = prc
case 6:
out[idx].EUR6 = prc
}
case "TRY":
switch grp {
case 1:
out[idx].TRY1 = prc
case 2:
out[idx].TRY2 = prc
case 3:
out[idx].TRY3 = prc
case 4:
out[idx].TRY4 = prc
case 5:
out[idx].TRY5 = prc
case 6:
out[idx].TRY6 = prc
}
}
}
if err := pgRows.Err(); err != nil {
_ = pgRows.Close()
return err
}
_ = pgRows.Close()
}
}
}
if pg := db.PgDB; pg != nil {
brandCodes := make([]string, 0, len(out))
seen := make(map[string]struct{}, len(out))
for _, it := range out {
code := strings.TrimSpace(it.BrandCode)
if code == "" {
continue
}
if _, ok := seen[code]; ok {
continue
}
seen[code] = struct{}{}
brandCodes = append(brandCodes, code)
}
for _, chunk := range chunkStringSlice(brandCodes, chunkSize) {
rows, err := pg.QueryContext(ctx, `
SELECT
m.brand_code,
COALESCE(g.title, '') AS grp_title
FROM mk_brandgrpmatch m
JOIN mk_brandgrp g ON g.id = m.grp_id
WHERE m.brand_code = ANY($1)
`, pq.Array(chunk))
if err != nil {
continue
}
grpByBrand := make(map[string]string, len(chunk))
for rows.Next() {
var code, title string
if err := rows.Scan(&code, &title); err != nil {
_ = rows.Close()
return err
}
grpByBrand[strings.TrimSpace(code)] = strings.TrimSpace(title)
}
if err := rows.Err(); err != nil {
_ = rows.Close()
return err
}
_ = rows.Close()
for i := range out {
if title, ok := grpByBrand[strings.TrimSpace(out[i].BrandCode)]; ok {
out[i].BrandGroupSec = title
}
}
}
}
return nil
}
func chunkStringSlice(values []string, size int) [][]string {
if size <= 0 {
size = 1000
}
out := make([][]string, 0, (len(values)+size-1)/size)
for start := 0; start < len(values); start += size {
end := start + size
if end > len(values) {
end = len(values)
}
out = append(out, values[start:end])
}
return out
}
func GetProductPricingPage(ctx context.Context, page int, limit int, filters ProductPricingFilters, includeTotal bool, sortBy string, descending bool) (ProductPricingPage, error) {
result := ProductPricingPage{
Rows: []models.ProductPricing{},