Merge remote-tracking branch 'origin/master'
This commit is contained in:
87
scripts/sql/product_filter_tr_cache_refresh.sql
Normal file
87
scripts/sql/product_filter_tr_cache_refresh.sql
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
Product filter cache refresh for Product Stock By Attributes endpoints.
|
||||||
|
This cache is used by backend queries when dbo.ProductFilterTRCache exists.
|
||||||
|
*/
|
||||||
|
|
||||||
|
USE BAGGI_V3;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('dbo.ProductFilterTRCache','U') IS NULL
|
||||||
|
BEGIN
|
||||||
|
CREATE TABLE dbo.ProductFilterTRCache
|
||||||
|
(
|
||||||
|
ProductCode NVARCHAR(50) NOT NULL PRIMARY KEY,
|
||||||
|
ProductDescription NVARCHAR(255) NULL,
|
||||||
|
ProductAtt01Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt02Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt11Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt38Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt41Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt44Desc NVARCHAR(255) NULL
|
||||||
|
);
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
TRUNCATE TABLE dbo.ProductFilterTRCache;
|
||||||
|
GO
|
||||||
|
|
||||||
|
INSERT INTO dbo.ProductFilterTRCache
|
||||||
|
(
|
||||||
|
ProductCode,
|
||||||
|
ProductDescription,
|
||||||
|
ProductAtt01Desc,
|
||||||
|
ProductAtt02Desc,
|
||||||
|
ProductAtt11Desc,
|
||||||
|
ProductAtt38Desc,
|
||||||
|
ProductAtt41Desc,
|
||||||
|
ProductAtt44Desc
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
ProductCode,
|
||||||
|
ProductDescription,
|
||||||
|
ProductAtt01Desc,
|
||||||
|
ProductAtt02Desc,
|
||||||
|
ProductAtt11Desc,
|
||||||
|
ProductAtt38Desc,
|
||||||
|
ProductAtt41Desc,
|
||||||
|
ProductAtt44Desc
|
||||||
|
FROM ProductFilterWithDescription('TR')
|
||||||
|
WHERE LEN(ProductCode) = 13;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_ProductFilterTRCache_Filter'
|
||||||
|
AND object_id = OBJECT_ID('dbo.ProductFilterTRCache')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
DROP INDEX IX_ProductFilterTRCache_Filter ON dbo.ProductFilterTRCache;
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_ProductFilterTRCache_KatAna'
|
||||||
|
AND object_id = OBJECT_ID('dbo.ProductFilterTRCache')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_ProductFilterTRCache_KatAna
|
||||||
|
ON dbo.ProductFilterTRCache (ProductAtt44Desc, ProductAtt01Desc, ProductCode)
|
||||||
|
INCLUDE (ProductDescription, ProductAtt02Desc, ProductAtt41Desc, ProductAtt38Desc, ProductAtt11Desc);
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1 FROM sys.indexes
|
||||||
|
WHERE name = 'IX_ProductFilterTRCache_KatAnaAlt'
|
||||||
|
AND object_id = OBJECT_ID('dbo.ProductFilterTRCache')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_ProductFilterTRCache_KatAnaAlt
|
||||||
|
ON dbo.ProductFilterTRCache (ProductAtt44Desc, ProductAtt01Desc, ProductAtt02Desc, ProductCode)
|
||||||
|
INCLUDE (ProductDescription, ProductAtt41Desc, ProductAtt38Desc, ProductAtt11Desc);
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
UPDATE STATISTICS dbo.ProductFilterTRCache WITH FULLSCAN;
|
||||||
|
GO
|
||||||
74
scripts/sql/product_stock_by_attributes_indexes.sql
Normal file
74
scripts/sql/product_stock_by_attributes_indexes.sql
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Performance indexes for Product Stock By Attributes queries.
|
||||||
|
Target: SQL Server
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* trStock (inventory aggregation) */
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.indexes
|
||||||
|
WHERE name = 'IX_trStock_Item_Warehouse_Dims'
|
||||||
|
AND object_id = OBJECT_ID('dbo.trStock')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_trStock_Item_Warehouse_Dims
|
||||||
|
ON dbo.trStock (ItemTypeCode, ItemCode, WarehouseCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code)
|
||||||
|
INCLUDE (In_Qty1, Out_Qty1, CompanyCode, OfficeCode, StoreTypeCode, StoreCode);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* PickingStates */
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.indexes
|
||||||
|
WHERE name = 'IX_PickingStates_Item_Warehouse_Dims'
|
||||||
|
AND object_id = OBJECT_ID('dbo.PickingStates')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_PickingStates_Item_Warehouse_Dims
|
||||||
|
ON dbo.PickingStates (ItemTypeCode, ItemCode, WarehouseCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code)
|
||||||
|
INCLUDE (Qty1, CompanyCode, OfficeCode, StoreTypeCode, StoreCode);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* ReserveStates */
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.indexes
|
||||||
|
WHERE name = 'IX_ReserveStates_Item_Warehouse_Dims'
|
||||||
|
AND object_id = OBJECT_ID('dbo.ReserveStates')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_ReserveStates_Item_Warehouse_Dims
|
||||||
|
ON dbo.ReserveStates (ItemTypeCode, ItemCode, WarehouseCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code)
|
||||||
|
INCLUDE (Qty1, CompanyCode, OfficeCode, StoreTypeCode, StoreCode);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* DispOrderStates */
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.indexes
|
||||||
|
WHERE name = 'IX_DispOrderStates_Item_Warehouse_Dims'
|
||||||
|
AND object_id = OBJECT_ID('dbo.DispOrderStates')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_DispOrderStates_Item_Warehouse_Dims
|
||||||
|
ON dbo.DispOrderStates (ItemTypeCode, ItemCode, WarehouseCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code)
|
||||||
|
INCLUDE (Qty1, CompanyCode, OfficeCode, StoreTypeCode, StoreCode);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
/* Latest price lookup */
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.indexes
|
||||||
|
WHERE name = 'IX_prItemBasePrice_ItemType_ItemCode_PriceDate'
|
||||||
|
AND object_id = OBJECT_ID('dbo.prItemBasePrice')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE NONCLUSTERED INDEX IX_prItemBasePrice_ItemType_ItemCode_PriceDate
|
||||||
|
ON dbo.prItemBasePrice (ItemTypeCode, ItemCode, PriceDate DESC)
|
||||||
|
INCLUDE (Price);
|
||||||
|
END;
|
||||||
|
GO
|
||||||
@@ -132,6 +132,7 @@ SELECT
|
|||||||
P.ProductAtt38Desc AS BIRINCI_PARCA_FIT,
|
P.ProductAtt38Desc AS BIRINCI_PARCA_FIT,
|
||||||
P.ProductAtt39Desc AS IKINCI_PARCA_FIT,
|
P.ProductAtt39Desc AS IKINCI_PARCA_FIT,
|
||||||
P.ProductAtt40Desc AS BOS2,
|
P.ProductAtt40Desc AS BOS2,
|
||||||
|
P.ProductAtt41Desc AS URUN_ICERIGI,
|
||||||
P.ProductAtt41Desc AS KISA_KAR,
|
P.ProductAtt41Desc AS KISA_KAR,
|
||||||
P.ProductAtt42Desc AS SERI_FASON,
|
P.ProductAtt42Desc AS SERI_FASON,
|
||||||
P.ProductAtt43Desc AS STOK_GIRIS_YONTEMI,
|
P.ProductAtt43Desc AS STOK_GIRIS_YONTEMI,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ DECLARE @Fit NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p7)), '');
|
|||||||
DECLARE @Drop NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p8)), '');
|
DECLARE @Drop NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p8)), '');
|
||||||
DECLARE @Beden NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p9)), '');
|
DECLARE @Beden NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p9)), '');
|
||||||
|
|
||||||
DECLARE @AttrBase TABLE
|
CREATE TABLE #AttrBase
|
||||||
(
|
(
|
||||||
ProductCode NVARCHAR(50) NOT NULL,
|
ProductCode NVARCHAR(50) NOT NULL,
|
||||||
Kategori NVARCHAR(100) NOT NULL,
|
Kategori NVARCHAR(100) NOT NULL,
|
||||||
@@ -24,7 +24,29 @@ DECLARE @AttrBase TABLE
|
|||||||
DropVal NVARCHAR(100) NOT NULL
|
DropVal NVARCHAR(100) NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO @AttrBase (ProductCode, Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal)
|
IF OBJECT_ID('dbo.ProductFilterTRCache','U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO #AttrBase (ProductCode, Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal)
|
||||||
|
SELECT
|
||||||
|
ProductCode,
|
||||||
|
Kategori = LTRIM(RTRIM(ProductAtt44Desc)),
|
||||||
|
UrunAnaGrubu = LTRIM(RTRIM(ProductAtt01Desc)),
|
||||||
|
UrunAltGrubu = LTRIM(RTRIM(ProductAtt02Desc)),
|
||||||
|
UrunIcerigi = LTRIM(RTRIM(ProductAtt41Desc)),
|
||||||
|
Fit = LTRIM(RTRIM(ProductAtt38Desc)),
|
||||||
|
DropVal = LTRIM(RTRIM(ProductAtt11Desc))
|
||||||
|
FROM dbo.ProductFilterTRCache
|
||||||
|
WHERE LEN(ProductCode) = 13
|
||||||
|
AND (@Kategori IS NULL OR ProductAtt44Desc = @Kategori)
|
||||||
|
AND (@UrunAnaGrubu IS NULL OR ProductAtt01Desc = @UrunAnaGrubu)
|
||||||
|
AND (@UrunAltGrubu IS NULL OR ProductAtt02Desc = @UrunAltGrubu)
|
||||||
|
AND (@UrunIcerigi IS NULL OR ProductAtt41Desc = @UrunIcerigi)
|
||||||
|
AND (@Fit IS NULL OR ProductAtt38Desc = @Fit)
|
||||||
|
AND (@Drop IS NULL OR ProductAtt11Desc = @Drop);
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO #AttrBase (ProductCode, Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal)
|
||||||
SELECT
|
SELECT
|
||||||
ProductCode,
|
ProductCode,
|
||||||
Kategori = LTRIM(RTRIM(ProductAtt44Desc)),
|
Kategori = LTRIM(RTRIM(ProductAtt44Desc)),
|
||||||
@@ -41,25 +63,30 @@ WHERE LEN(ProductCode) = 13
|
|||||||
AND (@UrunIcerigi IS NULL OR ProductAtt41Desc = @UrunIcerigi)
|
AND (@UrunIcerigi IS NULL OR ProductAtt41Desc = @UrunIcerigi)
|
||||||
AND (@Fit IS NULL OR ProductAtt38Desc = @Fit)
|
AND (@Fit IS NULL OR ProductAtt38Desc = @Fit)
|
||||||
AND (@Drop IS NULL OR ProductAtt11Desc = @Drop);
|
AND (@Drop IS NULL OR ProductAtt11Desc = @Drop);
|
||||||
|
END;
|
||||||
|
|
||||||
IF @Kategori IS NULL OR @UrunAnaGrubu IS NULL
|
IF @Kategori IS NULL OR @UrunAnaGrubu IS NULL
|
||||||
BEGIN
|
BEGIN
|
||||||
|
CREATE CLUSTERED INDEX IX_AttrBase_ProductCode ON #AttrBase(ProductCode);
|
||||||
SELECT 'kategori' AS FieldName, X.FieldValue
|
SELECT 'kategori' AS FieldName, X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.Kategori
|
SELECT DISTINCT FieldValue = AB.Kategori
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.Kategori <> ''
|
WHERE AB.Kategori <> ''
|
||||||
) X
|
) X
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 'urun_ana_grubu', X.FieldValue
|
SELECT 'urun_ana_grubu', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.UrunAnaGrubu
|
SELECT DISTINCT FieldValue = AB.UrunAnaGrubu
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.UrunAnaGrubu <> ''
|
WHERE AB.UrunAnaGrubu <> ''
|
||||||
) X;
|
) X;
|
||||||
RETURN;
|
RETURN;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
|
CREATE CLUSTERED INDEX IX_AttrBase_ProductCode ON #AttrBase(ProductCode);
|
||||||
|
CREATE NONCLUSTERED INDEX IX_AttrBase_Filter ON #AttrBase(Kategori, UrunAnaGrubu, UrunAltGrubu, UrunIcerigi, Fit, DropVal);
|
||||||
|
|
||||||
;WITH INV AS
|
;WITH INV AS
|
||||||
(
|
(
|
||||||
SELECT
|
SELECT
|
||||||
@@ -77,7 +104,7 @@ END;
|
|||||||
P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code,
|
P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code,
|
||||||
P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1
|
P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1
|
||||||
FROM PickingStates P
|
FROM PickingStates P
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = P.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = P.ItemCode
|
||||||
WHERE P.ItemTypeCode = 1
|
WHERE P.ItemTypeCode = 1
|
||||||
AND LEN(P.ItemCode) = 13
|
AND LEN(P.ItemCode) = 13
|
||||||
|
|
||||||
@@ -86,7 +113,7 @@ END;
|
|||||||
R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code,
|
R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code,
|
||||||
0, R.Qty1, 0, 0
|
0, R.Qty1, 0, 0
|
||||||
FROM ReserveStates R
|
FROM ReserveStates R
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = R.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = R.ItemCode
|
||||||
WHERE R.ItemTypeCode = 1
|
WHERE R.ItemTypeCode = 1
|
||||||
AND LEN(R.ItemCode) = 13
|
AND LEN(R.ItemCode) = 13
|
||||||
|
|
||||||
@@ -95,7 +122,7 @@ END;
|
|||||||
D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code,
|
D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code,
|
||||||
0, 0, D.Qty1, 0
|
0, 0, D.Qty1, 0
|
||||||
FROM DispOrderStates D
|
FROM DispOrderStates D
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = D.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = D.ItemCode
|
||||||
WHERE D.ItemTypeCode = 1
|
WHERE D.ItemTypeCode = 1
|
||||||
AND LEN(D.ItemCode) = 13
|
AND LEN(D.ItemCode) = 13
|
||||||
|
|
||||||
@@ -104,7 +131,7 @@ END;
|
|||||||
T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code,
|
T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code,
|
||||||
0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1)
|
0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1)
|
||||||
FROM trStock T WITH (NOLOCK)
|
FROM trStock T WITH (NOLOCK)
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = T.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = T.ItemCode
|
||||||
WHERE T.ItemTypeCode = 1
|
WHERE T.ItemTypeCode = 1
|
||||||
AND LEN(T.ItemCode) = 13
|
AND LEN(T.ItemCode) = 13
|
||||||
GROUP BY T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code
|
GROUP BY T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code
|
||||||
@@ -129,21 +156,21 @@ Avail AS
|
|||||||
SELECT 'kategori' AS FieldName, X.FieldValue
|
SELECT 'kategori' AS FieldName, X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.Kategori
|
SELECT DISTINCT FieldValue = AB.Kategori
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.Kategori <> ''
|
WHERE AB.Kategori <> ''
|
||||||
) X
|
) X
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 'urun_ana_grubu', X.FieldValue
|
SELECT 'urun_ana_grubu', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.UrunAnaGrubu
|
SELECT DISTINCT FieldValue = AB.UrunAnaGrubu
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.UrunAnaGrubu <> ''
|
WHERE AB.UrunAnaGrubu <> ''
|
||||||
) X
|
) X
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT 'urun_alt_grubu', X.FieldValue
|
SELECT 'urun_alt_grubu', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.UrunAltGrubu
|
SELECT DISTINCT FieldValue = AB.UrunAltGrubu
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.UrunAltGrubu <> ''
|
WHERE AB.UrunAltGrubu <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -153,7 +180,7 @@ SELECT 'renk', X.FieldValue
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END
|
SELECT DISTINCT FieldValue = CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END
|
||||||
FROM Avail A
|
FROM Avail A
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = A.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode
|
||||||
WHERE (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END) <> ''
|
WHERE (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END) <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -169,7 +196,7 @@ SELECT 'renk2', X.FieldValue
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = A.Renk2
|
SELECT DISTINCT FieldValue = A.Renk2
|
||||||
FROM Avail A
|
FROM Avail A
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = A.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode
|
||||||
WHERE A.Renk2 <> ''
|
WHERE A.Renk2 <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -184,7 +211,7 @@ UNION ALL
|
|||||||
SELECT 'urun_icerigi', X.FieldValue
|
SELECT 'urun_icerigi', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.UrunIcerigi
|
SELECT DISTINCT FieldValue = AB.UrunIcerigi
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.UrunIcerigi <> ''
|
WHERE AB.UrunIcerigi <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -193,7 +220,7 @@ UNION ALL
|
|||||||
SELECT 'fit', X.FieldValue
|
SELECT 'fit', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.Fit
|
SELECT DISTINCT FieldValue = AB.Fit
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.Fit <> ''
|
WHERE AB.Fit <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -202,7 +229,7 @@ UNION ALL
|
|||||||
SELECT 'drop', X.FieldValue
|
SELECT 'drop', X.FieldValue
|
||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = AB.DropVal
|
SELECT DISTINCT FieldValue = AB.DropVal
|
||||||
FROM @AttrBase AB
|
FROM #AttrBase AB
|
||||||
WHERE AB.DropVal <> ''
|
WHERE AB.DropVal <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -212,7 +239,7 @@ SELECT 'beden', X.FieldValue
|
|||||||
FROM (
|
FROM (
|
||||||
SELECT DISTINCT FieldValue = A.Beden
|
SELECT DISTINCT FieldValue = A.Beden
|
||||||
FROM Avail A
|
FROM Avail A
|
||||||
INNER JOIN @AttrBase AB ON AB.ProductCode = A.ItemCode
|
INNER JOIN #AttrBase AB ON AB.ProductCode = A.ItemCode
|
||||||
WHERE A.Beden <> ''
|
WHERE A.Beden <> ''
|
||||||
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
AND (@Kategori IS NULL OR AB.Kategori = @Kategori)
|
||||||
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
AND (@UrunAnaGrubu IS NULL OR AB.UrunAnaGrubu = @UrunAnaGrubu)
|
||||||
@@ -222,7 +249,8 @@ FROM (
|
|||||||
AND (@Drop IS NULL OR AB.DropVal = @Drop)
|
AND (@Drop IS NULL OR AB.DropVal = @Drop)
|
||||||
AND (@Renk IS NULL OR (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END) = @Renk)
|
AND (@Renk IS NULL OR (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.Renk END) = @Renk)
|
||||||
AND (@Renk2 IS NULL OR A.Renk2 = @Renk2)
|
AND (@Renk2 IS NULL OR A.Renk2 = @Renk2)
|
||||||
) X;
|
) X
|
||||||
|
OPTION (RECOMPILE);
|
||||||
`
|
`
|
||||||
|
|
||||||
// GetProductStockQueryByAttributes:
|
// GetProductStockQueryByAttributes:
|
||||||
@@ -238,8 +266,158 @@ DECLARE @Fit NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p7)), '');
|
|||||||
DECLARE @Drop NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p8)), '');
|
DECLARE @Drop NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p8)), '');
|
||||||
DECLARE @Beden NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p9)), '');
|
DECLARE @Beden NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p9)), '');
|
||||||
|
|
||||||
;WITH AttrFiltered AS
|
CREATE TABLE #AttrFiltered
|
||||||
(
|
(
|
||||||
|
ProductCode NVARCHAR(50) NOT NULL,
|
||||||
|
ProductDescription NVARCHAR(255) NULL,
|
||||||
|
ProductAtt01Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt02Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt10Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt11Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt21Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt22Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt23Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt24Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt25Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt26Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt27Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt28Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt29Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt30Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt31Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt32Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt33Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt34Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt35Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt36Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt37Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt38Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt39Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt40Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt41Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt42Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt43Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt44Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt45Desc NVARCHAR(255) NULL,
|
||||||
|
ProductAtt46Desc NVARCHAR(255) NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
IF OBJECT_ID('dbo.ProductFilterTRCache','U') IS NOT NULL
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO #AttrFiltered
|
||||||
|
(
|
||||||
|
ProductCode,
|
||||||
|
ProductDescription,
|
||||||
|
ProductAtt01Desc,
|
||||||
|
ProductAtt02Desc,
|
||||||
|
ProductAtt10Desc,
|
||||||
|
ProductAtt11Desc,
|
||||||
|
ProductAtt21Desc,
|
||||||
|
ProductAtt22Desc,
|
||||||
|
ProductAtt23Desc,
|
||||||
|
ProductAtt24Desc,
|
||||||
|
ProductAtt25Desc,
|
||||||
|
ProductAtt26Desc,
|
||||||
|
ProductAtt27Desc,
|
||||||
|
ProductAtt28Desc,
|
||||||
|
ProductAtt29Desc,
|
||||||
|
ProductAtt30Desc,
|
||||||
|
ProductAtt31Desc,
|
||||||
|
ProductAtt32Desc,
|
||||||
|
ProductAtt33Desc,
|
||||||
|
ProductAtt34Desc,
|
||||||
|
ProductAtt35Desc,
|
||||||
|
ProductAtt36Desc,
|
||||||
|
ProductAtt37Desc,
|
||||||
|
ProductAtt38Desc,
|
||||||
|
ProductAtt39Desc,
|
||||||
|
ProductAtt40Desc,
|
||||||
|
ProductAtt41Desc,
|
||||||
|
ProductAtt42Desc,
|
||||||
|
ProductAtt43Desc,
|
||||||
|
ProductAtt44Desc,
|
||||||
|
ProductAtt45Desc,
|
||||||
|
ProductAtt46Desc
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
C.ProductCode,
|
||||||
|
C.ProductDescription,
|
||||||
|
C.ProductAtt01Desc,
|
||||||
|
C.ProductAtt02Desc,
|
||||||
|
'' AS ProductAtt10Desc,
|
||||||
|
C.ProductAtt11Desc,
|
||||||
|
'' AS ProductAtt21Desc,
|
||||||
|
'' AS ProductAtt22Desc,
|
||||||
|
'' AS ProductAtt23Desc,
|
||||||
|
'' AS ProductAtt24Desc,
|
||||||
|
'' AS ProductAtt25Desc,
|
||||||
|
'' AS ProductAtt26Desc,
|
||||||
|
'' AS ProductAtt27Desc,
|
||||||
|
'' AS ProductAtt28Desc,
|
||||||
|
'' AS ProductAtt29Desc,
|
||||||
|
'' AS ProductAtt30Desc,
|
||||||
|
'' AS ProductAtt31Desc,
|
||||||
|
'' AS ProductAtt32Desc,
|
||||||
|
'' AS ProductAtt33Desc,
|
||||||
|
'' AS ProductAtt34Desc,
|
||||||
|
'' AS ProductAtt35Desc,
|
||||||
|
'' AS ProductAtt36Desc,
|
||||||
|
'' AS ProductAtt37Desc,
|
||||||
|
C.ProductAtt38Desc,
|
||||||
|
'' AS ProductAtt39Desc,
|
||||||
|
'' AS ProductAtt40Desc,
|
||||||
|
C.ProductAtt41Desc,
|
||||||
|
'' AS ProductAtt42Desc,
|
||||||
|
'' AS ProductAtt43Desc,
|
||||||
|
C.ProductAtt44Desc,
|
||||||
|
'' AS ProductAtt45Desc,
|
||||||
|
'' AS ProductAtt46Desc
|
||||||
|
FROM dbo.ProductFilterTRCache C
|
||||||
|
WHERE LEN(C.ProductCode) = 13
|
||||||
|
AND (@Kategori IS NULL OR C.ProductAtt44Desc = @Kategori)
|
||||||
|
AND (@UrunAnaGrubu IS NULL OR C.ProductAtt01Desc = @UrunAnaGrubu)
|
||||||
|
AND (@UrunAltGrubu IS NULL OR C.ProductAtt02Desc = @UrunAltGrubu)
|
||||||
|
AND (@UrunIcerigi IS NULL OR C.ProductAtt41Desc = @UrunIcerigi)
|
||||||
|
AND (@Fit IS NULL OR C.ProductAtt38Desc = @Fit)
|
||||||
|
AND (@Drop IS NULL OR C.ProductAtt11Desc = @Drop);
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO #AttrFiltered
|
||||||
|
(
|
||||||
|
ProductCode,
|
||||||
|
ProductDescription,
|
||||||
|
ProductAtt01Desc,
|
||||||
|
ProductAtt02Desc,
|
||||||
|
ProductAtt10Desc,
|
||||||
|
ProductAtt11Desc,
|
||||||
|
ProductAtt21Desc,
|
||||||
|
ProductAtt22Desc,
|
||||||
|
ProductAtt23Desc,
|
||||||
|
ProductAtt24Desc,
|
||||||
|
ProductAtt25Desc,
|
||||||
|
ProductAtt26Desc,
|
||||||
|
ProductAtt27Desc,
|
||||||
|
ProductAtt28Desc,
|
||||||
|
ProductAtt29Desc,
|
||||||
|
ProductAtt30Desc,
|
||||||
|
ProductAtt31Desc,
|
||||||
|
ProductAtt32Desc,
|
||||||
|
ProductAtt33Desc,
|
||||||
|
ProductAtt34Desc,
|
||||||
|
ProductAtt35Desc,
|
||||||
|
ProductAtt36Desc,
|
||||||
|
ProductAtt37Desc,
|
||||||
|
ProductAtt38Desc,
|
||||||
|
ProductAtt39Desc,
|
||||||
|
ProductAtt40Desc,
|
||||||
|
ProductAtt41Desc,
|
||||||
|
ProductAtt42Desc,
|
||||||
|
ProductAtt43Desc,
|
||||||
|
ProductAtt44Desc,
|
||||||
|
ProductAtt45Desc,
|
||||||
|
ProductAtt46Desc
|
||||||
|
)
|
||||||
SELECT
|
SELECT
|
||||||
ProductCode,
|
ProductCode,
|
||||||
ProductDescription,
|
ProductDescription,
|
||||||
@@ -280,8 +458,12 @@ DECLARE @Beden NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p9)), '');
|
|||||||
AND (@UrunAltGrubu IS NULL OR ProductAtt02Desc = @UrunAltGrubu)
|
AND (@UrunAltGrubu IS NULL OR ProductAtt02Desc = @UrunAltGrubu)
|
||||||
AND (@UrunIcerigi IS NULL OR ProductAtt41Desc = @UrunIcerigi)
|
AND (@UrunIcerigi IS NULL OR ProductAtt41Desc = @UrunIcerigi)
|
||||||
AND (@Fit IS NULL OR ProductAtt38Desc = @Fit)
|
AND (@Fit IS NULL OR ProductAtt38Desc = @Fit)
|
||||||
AND (@Drop IS NULL OR ProductAtt11Desc = @Drop)
|
AND (@Drop IS NULL OR ProductAtt11Desc = @Drop);
|
||||||
),
|
END;
|
||||||
|
|
||||||
|
CREATE CLUSTERED INDEX IX_AttrFiltered_ProductCode ON #AttrFiltered(ProductCode);
|
||||||
|
|
||||||
|
;WITH
|
||||||
INV AS
|
INV AS
|
||||||
(
|
(
|
||||||
SELECT
|
SELECT
|
||||||
@@ -307,9 +489,15 @@ INV AS
|
|||||||
P.ItemTypeCode, P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, P.ItemDim3Code,
|
P.ItemTypeCode, P.ItemCode, P.ColorCode, P.ItemDim1Code, P.ItemDim2Code, P.ItemDim3Code,
|
||||||
P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1
|
P.Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1
|
||||||
FROM PickingStates P
|
FROM PickingStates P
|
||||||
INNER JOIN AttrFiltered AF ON AF.ProductCode = P.ItemCode
|
INNER JOIN #AttrFiltered AF ON AF.ProductCode = P.ItemCode
|
||||||
WHERE P.ItemTypeCode = 1
|
WHERE P.ItemTypeCode = 1
|
||||||
AND LEN(P.ItemCode) = 13
|
AND LEN(P.ItemCode) = 13
|
||||||
|
AND P.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'
|
||||||
|
)
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
@@ -317,9 +505,15 @@ INV AS
|
|||||||
R.ItemTypeCode, R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, R.ItemDim3Code,
|
R.ItemTypeCode, R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, R.ItemDim3Code,
|
||||||
0, R.Qty1, 0, 0
|
0, R.Qty1, 0, 0
|
||||||
FROM ReserveStates R
|
FROM ReserveStates R
|
||||||
INNER JOIN AttrFiltered AF ON AF.ProductCode = R.ItemCode
|
INNER JOIN #AttrFiltered AF ON AF.ProductCode = R.ItemCode
|
||||||
WHERE R.ItemTypeCode = 1
|
WHERE R.ItemTypeCode = 1
|
||||||
AND LEN(R.ItemCode) = 13
|
AND LEN(R.ItemCode) = 13
|
||||||
|
AND R.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'
|
||||||
|
)
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
@@ -327,9 +521,15 @@ INV AS
|
|||||||
D.ItemTypeCode, D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, D.ItemDim3Code,
|
D.ItemTypeCode, D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, D.ItemDim3Code,
|
||||||
0, 0, D.Qty1, 0
|
0, 0, D.Qty1, 0
|
||||||
FROM DispOrderStates D
|
FROM DispOrderStates D
|
||||||
INNER JOIN AttrFiltered AF ON AF.ProductCode = D.ItemCode
|
INNER JOIN #AttrFiltered AF ON AF.ProductCode = D.ItemCode
|
||||||
WHERE D.ItemTypeCode = 1
|
WHERE D.ItemTypeCode = 1
|
||||||
AND LEN(D.ItemCode) = 13
|
AND LEN(D.ItemCode) = 13
|
||||||
|
AND D.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'
|
||||||
|
)
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT
|
SELECT
|
||||||
@@ -337,9 +537,15 @@ INV AS
|
|||||||
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code,
|
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code,
|
||||||
0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1)
|
0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1)
|
||||||
FROM trStock T WITH (NOLOCK)
|
FROM trStock T WITH (NOLOCK)
|
||||||
INNER JOIN AttrFiltered AF ON AF.ProductCode = T.ItemCode
|
INNER JOIN #AttrFiltered AF ON AF.ProductCode = T.ItemCode
|
||||||
WHERE T.ItemTypeCode = 1
|
WHERE T.ItemTypeCode = 1
|
||||||
AND LEN(T.ItemCode) = 13
|
AND LEN(T.ItemCode) = 13
|
||||||
|
AND T.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
|
GROUP BY
|
||||||
T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode,
|
T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode,
|
||||||
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code
|
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code
|
||||||
@@ -391,7 +597,7 @@ Grouped AS
|
|||||||
A.ColorCode,
|
A.ColorCode,
|
||||||
A.ItemDim2Code
|
A.ItemDim2Code
|
||||||
FROM Avail A
|
FROM Avail A
|
||||||
INNER JOIN AttrFiltered AF ON AF.ProductCode = A.ItemCode
|
INNER JOIN #AttrFiltered AF ON AF.ProductCode = A.ItemCode
|
||||||
WHERE (@Renk IS NULL OR (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.ColorCode END) = @Renk)
|
WHERE (@Renk IS NULL OR (CASE WHEN A.RenkAciklama <> '' THEN A.RenkAciklama ELSE A.ColorCode END) = @Renk)
|
||||||
AND (@Renk2 IS NULL OR A.ItemDim2Code = @Renk2)
|
AND (@Renk2 IS NULL OR A.ItemDim2Code = @Renk2)
|
||||||
AND (
|
AND (
|
||||||
@@ -454,7 +660,7 @@ INNER JOIN Grouped G
|
|||||||
ON G.ItemCode = A.ItemCode
|
ON G.ItemCode = A.ItemCode
|
||||||
AND G.ColorCode = A.ColorCode
|
AND G.ColorCode = A.ColorCode
|
||||||
AND ISNULL(G.ItemDim2Code, '') = ISNULL(A.ItemDim2Code, '')
|
AND ISNULL(G.ItemDim2Code, '') = ISNULL(A.ItemDim2Code, '')
|
||||||
INNER JOIN AttrFiltered AF
|
INNER JOIN #AttrFiltered AF
|
||||||
ON AF.ProductCode = A.ItemCode
|
ON AF.ProductCode = A.ItemCode
|
||||||
LEFT JOIN cdWarehouseDesc W WITH (NOLOCK)
|
LEFT JOIN cdWarehouseDesc W WITH (NOLOCK)
|
||||||
ON W.WarehouseCode = A.WarehouseCode
|
ON W.WarehouseCode = A.WarehouseCode
|
||||||
@@ -469,5 +675,6 @@ OUTER APPLY (
|
|||||||
AND PB.ItemCode = A.ItemCode
|
AND PB.ItemCode = A.ItemCode
|
||||||
AND LEN(PB.ItemCode) = 13
|
AND LEN(PB.ItemCode) = 13
|
||||||
ORDER BY PB.PriceDate DESC
|
ORDER BY PB.PriceDate DESC
|
||||||
) P;
|
) P
|
||||||
|
OPTION (RECOMPILE);
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -23,11 +23,53 @@ type ProductImageItem struct {
|
|||||||
ContentURL string `json:"content_url"`
|
ContentURL string `json:"content_url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tokenizeImageFileName(fileName string) []string {
|
||||||
|
up := strings.ToUpper(strings.TrimSpace(fileName))
|
||||||
|
if up == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return strings.FieldsFunc(up, func(r rune) bool {
|
||||||
|
isUpper := r >= 'A' && r <= 'Z'
|
||||||
|
isDigit := r >= '0' && r <= '9'
|
||||||
|
if isUpper || isDigit || r == '_' {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageFileMatches(fileName, color, secondColor string) bool {
|
||||||
|
color = strings.ToUpper(strings.TrimSpace(color))
|
||||||
|
secondColor = strings.ToUpper(strings.TrimSpace(secondColor))
|
||||||
|
if color == "" && secondColor == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens := tokenizeImageFileName(fileName)
|
||||||
|
if len(tokens) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
hasToken := func(target string) bool {
|
||||||
|
if target == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, t := range tokens {
|
||||||
|
if t == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasToken(color) && hasToken(secondColor)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// LIST PRODUCT IMAGES
|
// LIST PRODUCT IMAGES
|
||||||
//
|
//
|
||||||
|
|
||||||
// GET /api/product-images?code=...&color=...
|
// GET /api/product-images?code=...&color=...&yaka=...
|
||||||
func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc {
|
func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc {
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -41,6 +83,10 @@ func GetProductImagesHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
|
|
||||||
code := strings.TrimSpace(r.URL.Query().Get("code"))
|
code := strings.TrimSpace(r.URL.Query().Get("code"))
|
||||||
color := strings.TrimSpace(r.URL.Query().Get("color"))
|
color := strings.TrimSpace(r.URL.Query().Get("color"))
|
||||||
|
secondColor := strings.TrimSpace(r.URL.Query().Get("yaka"))
|
||||||
|
if secondColor == "" {
|
||||||
|
secondColor = strings.TrimSpace(r.URL.Query().Get("renk2"))
|
||||||
|
}
|
||||||
|
|
||||||
if code == "" {
|
if code == "" {
|
||||||
|
|
||||||
@@ -67,18 +113,13 @@ JOIN mmitem i
|
|||||||
WHERE b.typ = 'img'
|
WHERE b.typ = 'img'
|
||||||
AND b.src_table = 'mmitem'
|
AND b.src_table = 'mmitem'
|
||||||
AND UPPER(i.code) = UPPER($1)
|
AND UPPER(i.code) = UPPER($1)
|
||||||
AND (
|
|
||||||
$2 = ''
|
|
||||||
OR b.file_name ILIKE '%' || '-' || $2 || '-%'
|
|
||||||
OR b.file_name ILIKE '%' || '-' || $2 || '_%'
|
|
||||||
)
|
|
||||||
ORDER BY
|
ORDER BY
|
||||||
COALESCE(b.sort_order,999999),
|
COALESCE(b.sort_order,999999),
|
||||||
b.zlins_dttm DESC,
|
b.zlins_dttm DESC,
|
||||||
b.id DESC
|
b.id DESC
|
||||||
`
|
`
|
||||||
|
|
||||||
rows, err := pg.Query(query, code, color)
|
rows, err := pg.Query(query, code)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
@@ -86,6 +127,7 @@ ORDER BY
|
|||||||
"req_id", reqID,
|
"req_id", reqID,
|
||||||
"code", code,
|
"code", code,
|
||||||
"color", color,
|
"color", color,
|
||||||
|
"second_color", secondColor,
|
||||||
"err", err.Error(),
|
"err", err.Error(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -109,6 +151,9 @@ ORDER BY
|
|||||||
); err != nil {
|
); err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !imageFileMatches(it.FileName, color, secondColor) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
it.ContentURL = fmt.Sprintf("/api/product-images/%d/content", it.ID)
|
it.ContentURL = fmt.Sprintf("/api/product-images/%d/content", it.ID)
|
||||||
|
|
||||||
@@ -119,6 +164,7 @@ ORDER BY
|
|||||||
"req_id", reqID,
|
"req_id", reqID,
|
||||||
"code", code,
|
"code", code,
|
||||||
"color", color,
|
"color", color,
|
||||||
|
"second_color", secondColor,
|
||||||
"count", len(items),
|
"count", len(items),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -181,21 +181,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sub-image level2-image">
|
<div class="sub-image level2-image">
|
||||||
<q-card flat bordered class="product-image-card">
|
<q-card flat bordered class="product-image-card cursor-pointer" @click.stop="openProductCard(grp1, grp2)">
|
||||||
<q-card-section class="q-pa-xs product-image-wrap">
|
<q-card-section class="q-pa-xs product-image-wrap">
|
||||||
<q-img
|
<q-img
|
||||||
v-if="getProductImageUrl(grp1.productCode, grp2.colorCode)"
|
v-if="getProductImageUrl(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
:src="getProductImageUrl(grp1.productCode, grp2.colorCode)"
|
:src="getProductImageUrl(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
fit="cover"
|
fit="contain"
|
||||||
class="product-image"
|
class="product-image"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@error="onProductImageError(grp1.productCode, grp2.colorCode)"
|
@error="onProductImageError(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
/>
|
/>
|
||||||
<div v-else class="product-image-placeholder">
|
<div v-else class="product-image-placeholder">
|
||||||
<q-icon name="image_not_supported" size="22px" color="grey-6" />
|
<q-icon name="image_not_supported" size="22px" color="grey-6" />
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
label="Urun Detayi Gor"
|
||||||
|
class="detail-open-btn"
|
||||||
|
@click.stop="openProductCard(grp1, grp2)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -231,6 +239,99 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<q-dialog v-model="productCardDialog" maximized>
|
||||||
|
<q-card class="product-card-dialog">
|
||||||
|
<q-card-section class="row items-center q-pb-sm">
|
||||||
|
<div class="text-h6">Urun Karti</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-section class="q-pt-md">
|
||||||
|
<div class="product-card-stock">
|
||||||
|
<div class="text-subtitle1 text-weight-bold">
|
||||||
|
{{ productCardData.productCode || '-' }} / {{ productCardData.colorCode || '-' }}{{ productCardData.secondColor ? '-' + productCardData.secondColor : '' }}
|
||||||
|
</div>
|
||||||
|
<div class="text-caption">Toplam Stok: {{ formatNumber(productCardData.totalQty || 0) }}</div>
|
||||||
|
<div class="stock-size-grid q-mt-sm">
|
||||||
|
<div v-for="sz in sizeLabels" :key="'dlg-sz-' + sz" class="stock-size-chip">
|
||||||
|
<span class="label">{{ sz }}</span>
|
||||||
|
<span class="value">{{ Number(productCardData.sizeTotals?.[sz] || 0) > 0 ? formatNumber(productCardData.sizeTotals[sz]) : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-separator class="q-my-md" />
|
||||||
|
|
||||||
|
<div class="product-card-content">
|
||||||
|
<div class="product-card-images">
|
||||||
|
<q-carousel
|
||||||
|
v-if="productCardImages.length"
|
||||||
|
v-model="productCardSlide"
|
||||||
|
animated
|
||||||
|
swipeable
|
||||||
|
navigation
|
||||||
|
arrows
|
||||||
|
height="560px"
|
||||||
|
class="product-card-carousel rounded-borders bg-grey-2"
|
||||||
|
>
|
||||||
|
<q-carousel-slide
|
||||||
|
v-for="(img, idx) in productCardImages"
|
||||||
|
:key="'img-' + idx"
|
||||||
|
:name="idx"
|
||||||
|
class="column no-wrap flex-center"
|
||||||
|
>
|
||||||
|
<div class="dialog-image-stage cursor-pointer" @click="openProductImageFullscreen(img)">
|
||||||
|
<q-img :src="img" fit="contain" class="dialog-image" />
|
||||||
|
</div>
|
||||||
|
</q-carousel-slide>
|
||||||
|
</q-carousel>
|
||||||
|
<div v-else class="dialog-image-empty">
|
||||||
|
<q-icon name="image_not_supported" size="36px" color="grey-6" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="product-card-fields">
|
||||||
|
<div class="field-row"><span class="k">Urun Kodu</span><span class="v">{{ productCardData.productCode || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Renk</span><span class="v">{{ productCardData.colorCode || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun 2.Renk</span><span class="v">{{ productCardData.secondColor || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Kategori</span><span class="v">{{ productCardData.kategori || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Ana Grubu</span><span class="v">{{ productCardData.urunAnaGrubu || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Alt Grubu</span><span class="v">{{ productCardData.urunAltGrubu || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Icerigi</span><span class="v">{{ productCardData.urunIcerigi || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Fit</span><span class="v">{{ productCardData.fit || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Drop</span><span class="v">{{ productCardData.drop || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Kumas</span><span class="v">{{ productCardData.kumas || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Karisim</span><span class="v">{{ productCardData.karisim || '-' }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="productImageFullscreenDialog" maximized>
|
||||||
|
<q-card class="image-fullscreen-dialog">
|
||||||
|
<q-card-section class="row items-center q-pb-sm">
|
||||||
|
<div class="text-h6">Urun Fotografi</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
|
<q-card-section class="image-fullscreen-body">
|
||||||
|
<div class="image-fullscreen-stage cursor-pointer" @click="toggleFullscreenImageZoom">
|
||||||
|
<q-img
|
||||||
|
:src="productImageFullscreenSrc"
|
||||||
|
fit="contain"
|
||||||
|
class="image-fullscreen-img"
|
||||||
|
:style="fullscreenImageStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
|
||||||
<q-page v-else class="q-pa-md flex flex-center">
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
@@ -284,6 +385,7 @@ const filters = ref({
|
|||||||
})
|
})
|
||||||
const optionLists = ref({})
|
const optionLists = ref({})
|
||||||
const filteredOptionLists = ref({})
|
const filteredOptionLists = ref({})
|
||||||
|
const filterOptionsCache = ref({})
|
||||||
const rawRows = ref([])
|
const rawRows = ref([])
|
||||||
const productImageCache = ref({})
|
const productImageCache = ref({})
|
||||||
const productImageLoading = ref({})
|
const productImageLoading = ref({})
|
||||||
@@ -293,8 +395,19 @@ const productImageFallbackByKey = ref({})
|
|||||||
const productImageContentLoading = ref({})
|
const productImageContentLoading = ref({})
|
||||||
const productImageBlobUrls = ref([])
|
const productImageBlobUrls = ref([])
|
||||||
const productImageListBlockedUntil = ref(0)
|
const productImageListBlockedUntil = ref(0)
|
||||||
|
const productCardDialog = ref(false)
|
||||||
|
const productCardData = ref({})
|
||||||
|
const productCardImages = ref([])
|
||||||
|
const productCardSlide = ref(0)
|
||||||
|
const productImageFullscreenDialog = ref(false)
|
||||||
|
const productImageFullscreenSrc = ref('')
|
||||||
|
const productImageFullscreenZoom = ref(1)
|
||||||
const IMAGE_LIST_CONCURRENCY = 8
|
const IMAGE_LIST_CONCURRENCY = 8
|
||||||
|
const FILTER_OPTIONS_CACHE_TTL_MS = 60 * 1000
|
||||||
|
const FILTER_OPTIONS_DEBOUNCE_MS = 250
|
||||||
let imageListActiveRequests = 0
|
let imageListActiveRequests = 0
|
||||||
|
let filterOptionsDebounceTimer = null
|
||||||
|
let filterOptionsRequestSeq = 0
|
||||||
const imageListWaitQueue = []
|
const imageListWaitQueue = []
|
||||||
const activeSchema = ref(storeSchemaByKey.tak)
|
const activeSchema = ref(storeSchemaByKey.tak)
|
||||||
const activeGrpKey = ref('tak')
|
const activeGrpKey = ref('tak')
|
||||||
@@ -324,6 +437,11 @@ const allDetailsExpanded = computed(() => {
|
|||||||
const gridHeaderHeight = computed(() =>
|
const gridHeaderHeight = computed(() =>
|
||||||
showGridHeader.value ? '56px' : '0px'
|
showGridHeader.value ? '56px' : '0px'
|
||||||
)
|
)
|
||||||
|
const fullscreenImageStyle = computed(() => ({
|
||||||
|
transform: `scale(${productImageFullscreenZoom.value})`,
|
||||||
|
transformOrigin: 'center center',
|
||||||
|
transition: 'transform 0.15s ease-out'
|
||||||
|
}))
|
||||||
|
|
||||||
function emptySizeTotals() {
|
function emptySizeTotals() {
|
||||||
const map = {}
|
const map = {}
|
||||||
@@ -347,8 +465,20 @@ function sortByTotalQtyDesc(a, b) {
|
|||||||
return String(a?.key || '').localeCompare(String(b?.key || ''), 'tr', { sensitivity: 'base' })
|
return String(a?.key || '').localeCompare(String(b?.key || ''), 'tr', { sensitivity: 'base' })
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildImageKey(code, color) {
|
function buildImageKey(code, color, secondColor = '') {
|
||||||
return `${String(code || '').trim().toUpperCase()}::${String(color || '').trim().toUpperCase()}`
|
return `${String(code || '').trim().toUpperCase()}::${String(color || '').trim().toUpperCase()}::${String(secondColor || '').trim().toUpperCase()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageNameMatches(fileName, color, secondColor) {
|
||||||
|
const text = String(fileName || '').toUpperCase()
|
||||||
|
if (!text) return false
|
||||||
|
const tokens = text.replace(/[^A-Z0-9_]+/g, ' ').trim().split(/\s+/).filter(Boolean)
|
||||||
|
if (!tokens.length) return false
|
||||||
|
const colorTrim = String(color || '').trim().toUpperCase()
|
||||||
|
const secondTrim = String(secondColor || '').trim().toUpperCase()
|
||||||
|
if (colorTrim && !tokens.includes(colorTrim)) return false
|
||||||
|
if (secondTrim && !tokens.includes(secondTrim)) return false
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeUploadsPath(storagePath) {
|
function normalizeUploadsPath(storagePath) {
|
||||||
@@ -403,17 +533,17 @@ function resolveProductImageUrl(item) {
|
|||||||
return { contentUrl, publicUrl }
|
return { contentUrl, publicUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProductImageUrl(code, color) {
|
function getProductImageUrl(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const existing = productImageCache.value[key]
|
const existing = productImageCache.value[key]
|
||||||
if (existing !== undefined) return existing || ''
|
if (existing !== undefined) return existing || ''
|
||||||
|
|
||||||
void ensureProductImage(code, color)
|
void ensureProductImage(code, color, secondColor)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onProductImageError(code, color) {
|
async function onProductImageError(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const fallback = String(productImageFallbackByKey.value[key] || '')
|
const fallback = String(productImageFallbackByKey.value[key] || '')
|
||||||
if (fallback && !productImageContentLoading.value[key]) {
|
if (fallback && !productImageContentLoading.value[key]) {
|
||||||
productImageContentLoading.value[key] = true
|
productImageContentLoading.value[key] = true
|
||||||
@@ -438,10 +568,12 @@ async function onProductImageError(code, color) {
|
|||||||
productImageCache.value[key] = ''
|
productImageCache.value[key] = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureProductImage(code, color) {
|
async function ensureProductImage(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const codeTrim = String(code || '').trim().toUpperCase()
|
const codeTrim = String(code || '').trim().toUpperCase()
|
||||||
const colorTrim = String(color || '').trim().toUpperCase()
|
const colorTrim = String(color || '').trim().toUpperCase()
|
||||||
|
const secondTrim = String(secondColor || '').trim().toUpperCase()
|
||||||
|
const listKey = buildImageKey(codeTrim, colorTrim, secondTrim)
|
||||||
if (!codeTrim) {
|
if (!codeTrim) {
|
||||||
productImageCache.value[key] = ''
|
productImageCache.value[key] = ''
|
||||||
return ''
|
return ''
|
||||||
@@ -455,44 +587,44 @@ async function ensureProductImage(code, color) {
|
|||||||
|
|
||||||
productImageLoading.value[key] = true
|
productImageLoading.value[key] = true
|
||||||
try {
|
try {
|
||||||
if (!productImageListByCode.value[codeTrim]) {
|
if (!productImageListByCode.value[listKey]) {
|
||||||
if (!productImageListLoading.value[codeTrim]) {
|
if (!productImageListLoading.value[listKey]) {
|
||||||
productImageListLoading.value[codeTrim] = true
|
productImageListLoading.value[listKey] = true
|
||||||
try {
|
try {
|
||||||
if (imageListActiveRequests >= IMAGE_LIST_CONCURRENCY) {
|
if (imageListActiveRequests >= IMAGE_LIST_CONCURRENCY) {
|
||||||
await new Promise((resolve) => imageListWaitQueue.push(resolve))
|
await new Promise((resolve) => imageListWaitQueue.push(resolve))
|
||||||
}
|
}
|
||||||
imageListActiveRequests++
|
imageListActiveRequests++
|
||||||
const res = await api.get('/product-images', { params: { code: codeTrim } })
|
const params = { code: codeTrim, dim1: colorTrim, dim3: secondTrim }
|
||||||
productImageListByCode.value[codeTrim] = Array.isArray(res?.data) ? res.data : []
|
const res = await api.get('/product-images', { params })
|
||||||
|
productImageListByCode.value[listKey] = Array.isArray(res?.data) ? res.data : []
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
productImageListByCode.value[codeTrim] = []
|
productImageListByCode.value[listKey] = []
|
||||||
const status = Number(err?.response?.status || 0)
|
const status = Number(err?.response?.status || 0)
|
||||||
if (status >= 500 || status === 403 || status === 0) {
|
if (status >= 500 || status === 403 || status === 0) {
|
||||||
// Backend dengesizken istek firtinasini kisaca kes.
|
// Backend dengesizken istek firtinasini kisaca kes.
|
||||||
productImageListBlockedUntil.value = Date.now() + 30 * 1000
|
productImageListBlockedUntil.value = Date.now() + 30 * 1000
|
||||||
}
|
}
|
||||||
console.warn('[ProductStockByAttributes] product image list fetch failed', { code: codeTrim, err })
|
console.warn('[ProductStockByAttributes] product image list fetch failed', { code: codeTrim, color: colorTrim, secondColor: secondTrim, err })
|
||||||
} finally {
|
} finally {
|
||||||
imageListActiveRequests = Math.max(0, imageListActiveRequests - 1)
|
imageListActiveRequests = Math.max(0, imageListActiveRequests - 1)
|
||||||
const nextInQueue = imageListWaitQueue.shift()
|
const nextInQueue = imageListWaitQueue.shift()
|
||||||
if (nextInQueue) nextInQueue()
|
if (nextInQueue) nextInQueue()
|
||||||
delete productImageListLoading.value[codeTrim]
|
delete productImageListLoading.value[listKey]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ayni code icin baska bir istek zaten calisiyorsa tamamlanmasini bekle.
|
// Ayni code icin baska bir istek zaten calisiyorsa tamamlanmasini bekle.
|
||||||
while (productImageListLoading.value[codeTrim]) {
|
while (productImageListLoading.value[listKey]) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 25))
|
await new Promise((resolve) => setTimeout(resolve, 25))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const list = productImageListByCode.value[codeTrim] || []
|
const list = productImageListByCode.value[listKey] || []
|
||||||
|
|
||||||
let first = null
|
let first = null
|
||||||
if (colorTrim) {
|
if (colorTrim || secondTrim) {
|
||||||
const needle = `-${colorTrim.toLowerCase()}-`
|
|
||||||
first = list.find((item) =>
|
first = list.find((item) =>
|
||||||
String(item?.file_name || item?.FileName || '').toLowerCase().includes(needle)
|
imageNameMatches(String(item?.file_name || item?.FileName || ''), colorTrim, secondTrim)
|
||||||
) || null
|
) || null
|
||||||
}
|
}
|
||||||
if (!first) first = list[0] || null
|
if (!first) first = list[0] || null
|
||||||
@@ -625,6 +757,11 @@ const level1Groups = computed(() => {
|
|||||||
const depoAdi = String(item.Depo_Adi || '').trim()
|
const depoAdi = String(item.Depo_Adi || '').trim()
|
||||||
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
||||||
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
||||||
|
const urunIcerigi = String(item.URUN_ICERIGI || item.KISA_KAR || '').trim()
|
||||||
|
const fit = String(item.BIRINCI_PARCA_FIT || '').trim()
|
||||||
|
const drop = String(item.DR || '').trim()
|
||||||
|
const kumas = String(item.BIRINCI_PARCA_KUMAS || '').trim()
|
||||||
|
const karisim = String(item.BIRINCI_PARCA_KARISIM || '').trim()
|
||||||
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
||||||
const beden = normalizeSize(item.Beden || '')
|
const beden = normalizeSize(item.Beden || '')
|
||||||
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
||||||
@@ -654,6 +791,11 @@ const level1Groups = computed(() => {
|
|||||||
secondColor,
|
secondColor,
|
||||||
urunAnaGrubu,
|
urunAnaGrubu,
|
||||||
urunAltGrubu,
|
urunAltGrubu,
|
||||||
|
urunIcerigi,
|
||||||
|
fit,
|
||||||
|
drop,
|
||||||
|
kumas,
|
||||||
|
karisim,
|
||||||
aciklama,
|
aciklama,
|
||||||
sizeTotals: emptySizeTotals(),
|
sizeTotals: emptySizeTotals(),
|
||||||
totalQty: 0,
|
totalQty: 0,
|
||||||
@@ -714,6 +856,11 @@ function buildFilterParams() {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildFilterCacheKey(params) {
|
||||||
|
const keys = Object.keys(params || {}).sort()
|
||||||
|
return keys.map((k) => `${k}=${String(params[k] || '').trim()}`).join('&')
|
||||||
|
}
|
||||||
|
|
||||||
function isFilterDisabled(key) {
|
function isFilterDisabled(key) {
|
||||||
if (key === 'kategori') return false
|
if (key === 'kategori') return false
|
||||||
if (key === 'urun_ana_grubu') {
|
if (key === 'urun_ana_grubu') {
|
||||||
@@ -747,7 +894,12 @@ function onFilterValueChange(changedKey) {
|
|||||||
filters.value.beden = ''
|
filters.value.beden = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filterOptionsDebounceTimer) {
|
||||||
|
clearTimeout(filterOptionsDebounceTimer)
|
||||||
|
}
|
||||||
|
filterOptionsDebounceTimer = setTimeout(() => {
|
||||||
void loadFilterOptions()
|
void loadFilterOptions()
|
||||||
|
}, FILTER_OPTIONS_DEBOUNCE_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
function filterOptions(field, val, update) {
|
function filterOptions(field, val, update) {
|
||||||
@@ -768,12 +920,26 @@ function filterOptions(field, val, update) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFilterOptions() {
|
async function loadFilterOptions(force = false) {
|
||||||
|
const params = buildFilterParams()
|
||||||
|
const cacheKey = buildFilterCacheKey(params)
|
||||||
|
const now = Date.now()
|
||||||
|
if (!force) {
|
||||||
|
const cached = filterOptionsCache.value[cacheKey]
|
||||||
|
if (cached && Number(cached.expiresAt || 0) > now && cached.payload) {
|
||||||
|
optionLists.value = cached.payload.optionLists
|
||||||
|
filteredOptionLists.value = cached.payload.filteredOptionLists
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqSeq = ++filterOptionsRequestSeq
|
||||||
loadingFilterOptions.value = true
|
loadingFilterOptions.value = true
|
||||||
try {
|
try {
|
||||||
const res = await api.get('/product-stock-attribute-options', {
|
const res = await api.get('/product-stock-attribute-options', {
|
||||||
params: buildFilterParams()
|
params
|
||||||
})
|
})
|
||||||
|
if (reqSeq !== filterOptionsRequestSeq) return
|
||||||
const payload = res?.data && typeof res.data === 'object' ? res.data : {}
|
const payload = res?.data && typeof res.data === 'object' ? res.data : {}
|
||||||
const next = {}
|
const next = {}
|
||||||
const nextFiltered = {}
|
const nextFiltered = {}
|
||||||
@@ -795,13 +961,23 @@ async function loadFilterOptions() {
|
|||||||
|
|
||||||
optionLists.value = next
|
optionLists.value = next
|
||||||
filteredOptionLists.value = nextFiltered
|
filteredOptionLists.value = nextFiltered
|
||||||
|
filterOptionsCache.value[cacheKey] = {
|
||||||
|
expiresAt: now + FILTER_OPTIONS_CACHE_TTL_MS,
|
||||||
|
payload: {
|
||||||
|
optionLists: next,
|
||||||
|
filteredOptionLists: nextFiltered
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
if (reqSeq !== filterOptionsRequestSeq) return
|
||||||
errorMessage.value = 'Urun ozellik secenekleri alinamadi.'
|
errorMessage.value = 'Urun ozellik secenekleri alinamadi.'
|
||||||
console.error('loadFilterOptions error:', err)
|
console.error('loadFilterOptions error:', err)
|
||||||
} finally {
|
} finally {
|
||||||
|
if (reqSeq === filterOptionsRequestSeq) {
|
||||||
loadingFilterOptions.value = false
|
loadingFilterOptions.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function fetchStockByAttributes() {
|
async function fetchStockByAttributes() {
|
||||||
if (!canQuery.value) {
|
if (!canQuery.value) {
|
||||||
@@ -871,10 +1047,65 @@ async function fetchStockByAttributes() {
|
|||||||
function onLevel2Click(productCode, grp2) {
|
function onLevel2Click(productCode, grp2) {
|
||||||
toggleOpen(grp2.key)
|
toggleOpen(grp2.key)
|
||||||
if (isOpen(grp2.key)) {
|
if (isOpen(grp2.key)) {
|
||||||
void ensureProductImage(productCode, grp2.colorCode)
|
void ensureProductImage(productCode, grp2.colorCode, grp2.secondColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openProductCard(grp1, grp2) {
|
||||||
|
const productCode = String(grp1?.productCode || '').trim()
|
||||||
|
const colorCode = String(grp2?.colorCode || '').trim()
|
||||||
|
const secondColor = String(grp2?.secondColor || '').trim()
|
||||||
|
const listKey = buildImageKey(productCode, colorCode, secondColor)
|
||||||
|
|
||||||
|
await ensureProductImage(productCode, colorCode, secondColor)
|
||||||
|
const list = Array.isArray(productImageListByCode.value[listKey]) ? productImageListByCode.value[listKey] : []
|
||||||
|
const images = list
|
||||||
|
.map((item) => {
|
||||||
|
const resolved = resolveProductImageUrl(item)
|
||||||
|
return resolved.contentUrl || resolved.publicUrl || ''
|
||||||
|
})
|
||||||
|
.filter((x) => String(x || '').trim() !== '')
|
||||||
|
|
||||||
|
if (!images.length) {
|
||||||
|
const single = getProductImageUrl(productCode, colorCode, secondColor)
|
||||||
|
if (single) images.push(single)
|
||||||
|
}
|
||||||
|
|
||||||
|
productCardImages.value = images
|
||||||
|
productCardSlide.value = 0
|
||||||
|
productCardData.value = {
|
||||||
|
productCode,
|
||||||
|
colorCode,
|
||||||
|
secondColor,
|
||||||
|
kategori: String(filters.value?.kategori || '').trim(),
|
||||||
|
urunAnaGrubu: String(grp2?.urunAnaGrubu || '').trim(),
|
||||||
|
urunAltGrubu: String(grp2?.urunAltGrubu || '').trim(),
|
||||||
|
urunIcerigi: String(grp2?.urunIcerigi || '').trim(),
|
||||||
|
fit: String(grp2?.fit || '').trim(),
|
||||||
|
drop: String(grp2?.drop || '').trim(),
|
||||||
|
kumas: String(grp2?.kumas || '').trim(),
|
||||||
|
karisim: String(grp2?.karisim || '').trim(),
|
||||||
|
sizeTotals: grp2?.sizeTotals || {},
|
||||||
|
totalQty: Number(grp2?.totalQty || 0)
|
||||||
|
}
|
||||||
|
productCardDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openProductImageFullscreen(src) {
|
||||||
|
const value = String(src || '').trim()
|
||||||
|
if (!value) return
|
||||||
|
productImageFullscreenSrc.value = value
|
||||||
|
productImageFullscreenZoom.value = 1
|
||||||
|
productImageFullscreenDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFullscreenImageZoom() {
|
||||||
|
const current = Number(productImageFullscreenZoom.value || 1)
|
||||||
|
if (current < 1.5) productImageFullscreenZoom.value = 1.8
|
||||||
|
else if (current < 2.3) productImageFullscreenZoom.value = 2.6
|
||||||
|
else productImageFullscreenZoom.value = 1
|
||||||
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
filters.value = {
|
filters.value = {
|
||||||
kategori: '',
|
kategori: '',
|
||||||
@@ -898,14 +1129,26 @@ function resetForm() {
|
|||||||
productImageFallbackByKey.value = {}
|
productImageFallbackByKey.value = {}
|
||||||
productImageContentLoading.value = {}
|
productImageContentLoading.value = {}
|
||||||
productImageListBlockedUntil.value = 0
|
productImageListBlockedUntil.value = 0
|
||||||
void loadFilterOptions()
|
productCardDialog.value = false
|
||||||
|
productCardData.value = {}
|
||||||
|
productCardImages.value = []
|
||||||
|
productCardSlide.value = 0
|
||||||
|
productImageFullscreenDialog.value = false
|
||||||
|
productImageFullscreenSrc.value = ''
|
||||||
|
productImageFullscreenZoom.value = 1
|
||||||
|
filterOptionsCache.value = {}
|
||||||
|
void loadFilterOptions(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadFilterOptions()
|
void loadFilterOptions(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
|
if (filterOptionsDebounceTimer) {
|
||||||
|
clearTimeout(filterOptionsDebounceTimer)
|
||||||
|
filterOptionsDebounceTimer = null
|
||||||
|
}
|
||||||
for (const url of productImageBlobUrls.value) {
|
for (const url of productImageBlobUrls.value) {
|
||||||
try { URL.revokeObjectURL(url) } catch {}
|
try { URL.revokeObjectURL(url) } catch {}
|
||||||
}
|
}
|
||||||
@@ -920,7 +1163,7 @@ onUnmounted(() => {
|
|||||||
--grp-title-w: 44px;
|
--grp-title-w: 44px;
|
||||||
--psq-header-h: 56px;
|
--psq-header-h: 56px;
|
||||||
--psq-col-adet: calc(var(--col-adet) + var(--beden-w));
|
--psq-col-adet: calc(var(--col-adet) + var(--beden-w));
|
||||||
--psq-col-img: 126px;
|
--psq-col-img: 190px;
|
||||||
--psq-l1-lift: 42px;
|
--psq-l1-lift: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,8 +1246,8 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-sub-header.level-2 {
|
.order-sub-header.level-2 {
|
||||||
min-height: 82px !important;
|
min-height: 252px !important;
|
||||||
height: 82px !important;
|
height: 252px !important;
|
||||||
background: #fff9c4 !important;
|
background: #fff9c4 !important;
|
||||||
border-top: 1px solid #d4c79f !important;
|
border-top: 1px solid #d4c79f !important;
|
||||||
border-bottom: 1px solid #d4c79f !important;
|
border-bottom: 1px solid #d4c79f !important;
|
||||||
@@ -1250,16 +1493,18 @@ onUnmounted(() => {
|
|||||||
.order-sub-header.level-2 .sub-image.level2-image {
|
.order-sub-header.level-2 .sub-image.level2-image {
|
||||||
grid-column: 8 / 9;
|
grid-column: 8 / 9;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
border-left: 1px solid #d4c79f;
|
border-left: 1px solid #d4c79f;
|
||||||
padding: 0 6px;
|
gap: 8px;
|
||||||
|
padding: 8px 10px;
|
||||||
background: #fffef7;
|
background: #fffef7;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image-card {
|
.product-image-card {
|
||||||
width: 108px;
|
width: 162px;
|
||||||
height: 66px;
|
height: 216px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1272,6 +1517,7 @@ onUnmounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image-placeholder {
|
.product-image-placeholder {
|
||||||
@@ -1284,6 +1530,178 @@ onUnmounted(() => {
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-open-btn {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-dialog {
|
||||||
|
background: #fffef9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-stock {
|
||||||
|
background: #f8f5e7;
|
||||||
|
border: 1px solid #e2d9b6;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-chip {
|
||||||
|
border: 1px solid #d8cca6;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 6px 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-chip .label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(360px, 1fr) 420px;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-images {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
min-height: 560px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-carousel {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image-stage {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 560px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f7f4e9;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image-empty {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 560px;
|
||||||
|
border: 1px dashed #cabf9a;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #faf7ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-dialog {
|
||||||
|
background: #f4f0e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-body {
|
||||||
|
height: calc(100vh - 72px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-stage {
|
||||||
|
width: min(96vw, 1400px);
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #efe7cc;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-fields {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
border: 1px solid #e2d9b6;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
height: 560px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 7px 0;
|
||||||
|
border-bottom: 1px solid #f0ead7;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row .k {
|
||||||
|
color: #5a4f2c;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row .v {
|
||||||
|
color: #1f1f1f;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-btn,
|
||||||
|
.q-icon,
|
||||||
|
.product-image-card,
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.product-card-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-images,
|
||||||
|
.product-card-fields {
|
||||||
|
grid-column: auto;
|
||||||
|
grid-row: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-fields {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.order-sub-header.level-2 .sub-right .top-total {
|
.order-sub-header.level-2 .sub-right .top-total {
|
||||||
grid-column: 1 / 2;
|
grid-column: 1 / 2;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
|||||||
@@ -179,21 +179,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="sub-image level2-image">
|
<div class="sub-image level2-image">
|
||||||
<q-card flat bordered class="product-image-card">
|
<q-card flat bordered class="product-image-card cursor-pointer" @click.stop="openProductCard(grp1, grp2)">
|
||||||
<q-card-section class="q-pa-xs product-image-wrap">
|
<q-card-section class="q-pa-xs product-image-wrap">
|
||||||
<q-img
|
<q-img
|
||||||
v-if="getProductImageUrl(grp1.productCode, grp2.colorCode)"
|
v-if="getProductImageUrl(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
:src="getProductImageUrl(grp1.productCode, grp2.colorCode)"
|
:src="getProductImageUrl(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
fit="cover"
|
fit="contain"
|
||||||
class="product-image"
|
class="product-image"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
@error="onProductImageError(grp1.productCode, grp2.colorCode)"
|
@error="onProductImageError(grp1.productCode, grp2.colorCode, grp2.secondColor)"
|
||||||
/>
|
/>
|
||||||
<div v-else class="product-image-placeholder">
|
<div v-else class="product-image-placeholder">
|
||||||
<q-icon name="image_not_supported" size="22px" color="grey-6" />
|
<q-icon name="image_not_supported" size="22px" color="grey-6" />
|
||||||
</div>
|
</div>
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
label="Urun Detayi Gor"
|
||||||
|
class="detail-open-btn"
|
||||||
|
@click.stop="openProductCard(grp1, grp2)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -229,6 +237,99 @@
|
|||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<q-dialog v-model="productCardDialog" maximized>
|
||||||
|
<q-card class="product-card-dialog">
|
||||||
|
<q-card-section class="row items-center q-pb-sm">
|
||||||
|
<div class="text-h6">Urun Karti</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-section class="q-pt-md">
|
||||||
|
<div class="product-card-stock">
|
||||||
|
<div class="text-subtitle1 text-weight-bold">
|
||||||
|
{{ productCardData.productCode || '-' }} / {{ productCardData.colorCode || '-' }}{{ productCardData.secondColor ? '-' + productCardData.secondColor : '' }}
|
||||||
|
</div>
|
||||||
|
<div class="text-caption">Toplam Stok: {{ formatNumber(productCardData.totalQty || 0) }}</div>
|
||||||
|
<div class="stock-size-grid q-mt-sm">
|
||||||
|
<div v-for="sz in sizeLabels" :key="'dlg-sz-' + sz" class="stock-size-chip">
|
||||||
|
<span class="label">{{ sz }}</span>
|
||||||
|
<span class="value">{{ Number(productCardData.sizeTotals?.[sz] || 0) > 0 ? formatNumber(productCardData.sizeTotals[sz]) : '-' }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-separator class="q-my-md" />
|
||||||
|
|
||||||
|
<div class="product-card-content">
|
||||||
|
<div class="product-card-images">
|
||||||
|
<q-carousel
|
||||||
|
v-if="productCardImages.length"
|
||||||
|
v-model="productCardSlide"
|
||||||
|
animated
|
||||||
|
swipeable
|
||||||
|
navigation
|
||||||
|
arrows
|
||||||
|
height="560px"
|
||||||
|
class="product-card-carousel rounded-borders bg-grey-2"
|
||||||
|
>
|
||||||
|
<q-carousel-slide
|
||||||
|
v-for="(img, idx) in productCardImages"
|
||||||
|
:key="'img-' + idx"
|
||||||
|
:name="idx"
|
||||||
|
class="column no-wrap flex-center"
|
||||||
|
>
|
||||||
|
<div class="dialog-image-stage cursor-pointer" @click="openProductImageFullscreen(img)">
|
||||||
|
<q-img :src="img" fit="contain" class="dialog-image" />
|
||||||
|
</div>
|
||||||
|
</q-carousel-slide>
|
||||||
|
</q-carousel>
|
||||||
|
<div v-else class="dialog-image-empty">
|
||||||
|
<q-icon name="image_not_supported" size="36px" color="grey-6" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="product-card-fields">
|
||||||
|
<div class="field-row"><span class="k">Urun Kodu</span><span class="v">{{ productCardData.productCode || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Renk</span><span class="v">{{ productCardData.colorCode || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun 2.Renk</span><span class="v">{{ productCardData.secondColor || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Kategori</span><span class="v">{{ productCardData.kategori || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Ana Grubu</span><span class="v">{{ productCardData.urunAnaGrubu || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Alt Grubu</span><span class="v">{{ productCardData.urunAltGrubu || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Urun Icerigi</span><span class="v">{{ productCardData.urunIcerigi || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Fit</span><span class="v">{{ productCardData.fit || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Drop</span><span class="v">{{ productCardData.drop || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Kumas</span><span class="v">{{ productCardData.kumas || '-' }}</span></div>
|
||||||
|
<div class="field-row"><span class="k">Karisim</span><span class="v">{{ productCardData.karisim || '-' }}</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="productImageFullscreenDialog" maximized>
|
||||||
|
<q-card class="image-fullscreen-dialog">
|
||||||
|
<q-card-section class="row items-center q-pb-sm">
|
||||||
|
<div class="text-h6">Urun Fotografi</div>
|
||||||
|
<q-space />
|
||||||
|
<q-btn icon="close" flat round dense v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
<q-separator />
|
||||||
|
<q-card-section class="image-fullscreen-body">
|
||||||
|
<div class="image-fullscreen-stage cursor-pointer" @click="toggleFullscreenImageZoom">
|
||||||
|
<q-img
|
||||||
|
:src="productImageFullscreenSrc"
|
||||||
|
fit="contain"
|
||||||
|
class="image-fullscreen-img"
|
||||||
|
:style="fullscreenImageStyle"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
</q-page>
|
</q-page>
|
||||||
|
|
||||||
<q-page v-else class="q-pa-md flex flex-center">
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
@@ -269,6 +370,13 @@ const productImageFallbackByKey = ref({})
|
|||||||
const productImageContentLoading = ref({})
|
const productImageContentLoading = ref({})
|
||||||
const productImageBlobUrls = ref([])
|
const productImageBlobUrls = ref([])
|
||||||
const productImageListBlockedUntil = ref(0)
|
const productImageListBlockedUntil = ref(0)
|
||||||
|
const productCardDialog = ref(false)
|
||||||
|
const productCardData = ref({})
|
||||||
|
const productCardImages = ref([])
|
||||||
|
const productCardSlide = ref(0)
|
||||||
|
const productImageFullscreenDialog = ref(false)
|
||||||
|
const productImageFullscreenSrc = ref('')
|
||||||
|
const productImageFullscreenZoom = ref(1)
|
||||||
const IMAGE_LIST_CONCURRENCY = 8
|
const IMAGE_LIST_CONCURRENCY = 8
|
||||||
let imageListActiveRequests = 0
|
let imageListActiveRequests = 0
|
||||||
const imageListWaitQueue = []
|
const imageListWaitQueue = []
|
||||||
@@ -296,6 +404,11 @@ const allDetailsExpanded = computed(() => {
|
|||||||
const gridHeaderHeight = computed(() =>
|
const gridHeaderHeight = computed(() =>
|
||||||
showGridHeader.value ? '56px' : '0px'
|
showGridHeader.value ? '56px' : '0px'
|
||||||
)
|
)
|
||||||
|
const fullscreenImageStyle = computed(() => ({
|
||||||
|
transform: `scale(${productImageFullscreenZoom.value})`,
|
||||||
|
transformOrigin: 'center center',
|
||||||
|
transition: 'transform 0.15s ease-out'
|
||||||
|
}))
|
||||||
|
|
||||||
function emptySizeTotals() {
|
function emptySizeTotals() {
|
||||||
const map = {}
|
const map = {}
|
||||||
@@ -312,8 +425,53 @@ function parseNumber(value) {
|
|||||||
return Number.isFinite(n) ? n : 0
|
return Number.isFinite(n) ? n : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildImageKey(code, color) {
|
function sortByColorCodeAsc(a, b) {
|
||||||
return `${String(code || '').trim().toUpperCase()}::${String(color || '').trim().toUpperCase()}`
|
const compareCodeLike = (va, vb) => {
|
||||||
|
const sa = String(va || '').trim()
|
||||||
|
const sb = String(vb || '').trim()
|
||||||
|
const pa = sa.match(/^(\d+)(?:_(\d+))?$/)
|
||||||
|
const pb = sb.match(/^(\d+)(?:_(\d+))?$/)
|
||||||
|
if (pa && pb) {
|
||||||
|
const a1 = Number.parseInt(pa[1], 10)
|
||||||
|
const b1 = Number.parseInt(pb[1], 10)
|
||||||
|
if (a1 !== b1) return a1 - b1
|
||||||
|
const a2 = Number.parseInt(pa[2] || '0', 10)
|
||||||
|
const b2 = Number.parseInt(pb[2] || '0', 10)
|
||||||
|
if (a2 !== b2) return a2 - b2
|
||||||
|
}
|
||||||
|
return sa.localeCompare(sb, 'tr', { sensitivity: 'base' })
|
||||||
|
}
|
||||||
|
|
||||||
|
const ca = String(a?.colorCode || '').trim()
|
||||||
|
const cb = String(b?.colorCode || '').trim()
|
||||||
|
const na = Number.parseInt(ca, 10)
|
||||||
|
const nb = Number.parseInt(cb, 10)
|
||||||
|
const aNum = Number.isFinite(na)
|
||||||
|
const bNum = Number.isFinite(nb)
|
||||||
|
|
||||||
|
if (aNum && bNum && na !== nb) return na - nb
|
||||||
|
if (aNum !== bNum) return aNum ? -1 : 1
|
||||||
|
|
||||||
|
const cmp = compareCodeLike(ca, cb)
|
||||||
|
if (cmp !== 0) return cmp
|
||||||
|
|
||||||
|
return compareCodeLike(a?.secondColor, b?.secondColor)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildImageKey(code, color, secondColor = '') {
|
||||||
|
return `${String(code || '').trim().toUpperCase()}::${String(color || '').trim().toUpperCase()}::${String(secondColor || '').trim().toUpperCase()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function imageNameMatches(fileName, color, secondColor) {
|
||||||
|
const text = String(fileName || '').toUpperCase()
|
||||||
|
if (!text) return false
|
||||||
|
const tokens = text.replace(/[^A-Z0-9_]+/g, ' ').trim().split(/\s+/).filter(Boolean)
|
||||||
|
if (!tokens.length) return false
|
||||||
|
const colorTrim = String(color || '').trim().toUpperCase()
|
||||||
|
const secondTrim = String(secondColor || '').trim().toUpperCase()
|
||||||
|
if (colorTrim && !tokens.includes(colorTrim)) return false
|
||||||
|
if (secondTrim && !tokens.includes(secondTrim)) return false
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeUploadsPath(storagePath) {
|
function normalizeUploadsPath(storagePath) {
|
||||||
@@ -358,16 +516,16 @@ function resolveProductImageUrl(item) {
|
|||||||
return { contentUrl, publicUrl }
|
return { contentUrl, publicUrl }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProductImageUrl(code, color) {
|
function getProductImageUrl(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const existing = productImageCache.value[key]
|
const existing = productImageCache.value[key]
|
||||||
if (existing !== undefined) return existing || ''
|
if (existing !== undefined) return existing || ''
|
||||||
void ensureProductImage(code, color)
|
void ensureProductImage(code, color, secondColor)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onProductImageError(code, color) {
|
async function onProductImageError(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const fallback = String(productImageFallbackByKey.value[key] || '')
|
const fallback = String(productImageFallbackByKey.value[key] || '')
|
||||||
if (fallback && !productImageContentLoading.value[key]) {
|
if (fallback && !productImageContentLoading.value[key]) {
|
||||||
productImageContentLoading.value[key] = true
|
productImageContentLoading.value[key] = true
|
||||||
@@ -389,10 +547,12 @@ async function onProductImageError(code, color) {
|
|||||||
productImageCache.value[key] = ''
|
productImageCache.value[key] = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
async function ensureProductImage(code, color) {
|
async function ensureProductImage(code, color, secondColor = '') {
|
||||||
const key = buildImageKey(code, color)
|
const key = buildImageKey(code, color, secondColor)
|
||||||
const codeTrim = String(code || '').trim().toUpperCase()
|
const codeTrim = String(code || '').trim().toUpperCase()
|
||||||
const colorTrim = String(color || '').trim().toUpperCase()
|
const colorTrim = String(color || '').trim().toUpperCase()
|
||||||
|
const secondTrim = String(secondColor || '').trim().toUpperCase()
|
||||||
|
const listKey = buildImageKey(codeTrim, colorTrim, secondTrim)
|
||||||
if (!codeTrim) {
|
if (!codeTrim) {
|
||||||
productImageCache.value[key] = ''
|
productImageCache.value[key] = ''
|
||||||
return ''
|
return ''
|
||||||
@@ -406,42 +566,42 @@ async function ensureProductImage(code, color) {
|
|||||||
|
|
||||||
productImageLoading.value[key] = true
|
productImageLoading.value[key] = true
|
||||||
try {
|
try {
|
||||||
if (!productImageListByCode.value[codeTrim]) {
|
if (!productImageListByCode.value[listKey]) {
|
||||||
if (!productImageListLoading.value[codeTrim]) {
|
if (!productImageListLoading.value[listKey]) {
|
||||||
productImageListLoading.value[codeTrim] = true
|
productImageListLoading.value[listKey] = true
|
||||||
try {
|
try {
|
||||||
if (imageListActiveRequests >= IMAGE_LIST_CONCURRENCY) {
|
if (imageListActiveRequests >= IMAGE_LIST_CONCURRENCY) {
|
||||||
await new Promise((resolve) => imageListWaitQueue.push(resolve))
|
await new Promise((resolve) => imageListWaitQueue.push(resolve))
|
||||||
}
|
}
|
||||||
imageListActiveRequests++
|
imageListActiveRequests++
|
||||||
const res = await api.get('/product-images', { params: { code: codeTrim } })
|
const params = { code: codeTrim, dim1: colorTrim, dim3: secondTrim }
|
||||||
productImageListByCode.value[codeTrim] = Array.isArray(res?.data) ? res.data : []
|
const res = await api.get('/product-images', { params })
|
||||||
|
productImageListByCode.value[listKey] = Array.isArray(res?.data) ? res.data : []
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
productImageListByCode.value[codeTrim] = []
|
productImageListByCode.value[listKey] = []
|
||||||
const status = Number(err?.response?.status || 0)
|
const status = Number(err?.response?.status || 0)
|
||||||
if (status >= 500 || status === 403 || status === 0) {
|
if (status >= 500 || status === 403 || status === 0) {
|
||||||
productImageListBlockedUntil.value = Date.now() + 30 * 1000
|
productImageListBlockedUntil.value = Date.now() + 30 * 1000
|
||||||
}
|
}
|
||||||
console.warn('[ProductStockQuery] product image list fetch failed', { code: codeTrim, err })
|
console.warn('[ProductStockQuery] product image list fetch failed', { code: codeTrim, color: colorTrim, secondColor: secondTrim, err })
|
||||||
} finally {
|
} finally {
|
||||||
imageListActiveRequests = Math.max(0, imageListActiveRequests - 1)
|
imageListActiveRequests = Math.max(0, imageListActiveRequests - 1)
|
||||||
const nextInQueue = imageListWaitQueue.shift()
|
const nextInQueue = imageListWaitQueue.shift()
|
||||||
if (nextInQueue) nextInQueue()
|
if (nextInQueue) nextInQueue()
|
||||||
delete productImageListLoading.value[codeTrim]
|
delete productImageListLoading.value[listKey]
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (productImageListLoading.value[codeTrim]) {
|
while (productImageListLoading.value[listKey]) {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 25))
|
await new Promise((resolve) => setTimeout(resolve, 25))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = productImageListByCode.value[codeTrim] || []
|
const list = productImageListByCode.value[listKey] || []
|
||||||
let first = null
|
let first = null
|
||||||
if (colorTrim) {
|
if (colorTrim || secondTrim) {
|
||||||
const needle = `-${colorTrim.toLowerCase()}-`
|
|
||||||
first = list.find((item) =>
|
first = list.find((item) =>
|
||||||
String(item?.file_name || item?.FileName || '').toLowerCase().includes(needle)
|
imageNameMatches(String(item?.file_name || item?.FileName || ''), colorTrim, secondTrim)
|
||||||
) || null
|
) || null
|
||||||
}
|
}
|
||||||
if (!first) first = list[0] || null
|
if (!first) first = list[0] || null
|
||||||
@@ -571,8 +731,14 @@ const level1Groups = computed(() => {
|
|||||||
const secondColor = String(item.Yaka || '').trim()
|
const secondColor = String(item.Yaka || '').trim()
|
||||||
const depoKodu = String(item.Depo_Kodu || '').trim()
|
const depoKodu = String(item.Depo_Kodu || '').trim()
|
||||||
const depoAdi = String(item.Depo_Adi || '').trim()
|
const depoAdi = String(item.Depo_Adi || '').trim()
|
||||||
|
const kategori = String(item.YETISKIN_GARSON || '').trim()
|
||||||
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
||||||
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
||||||
|
const urunIcerigi = String(item.URUN_ICERIGI || item.KISA_KAR || '').trim()
|
||||||
|
const fit = String(item.BIRINCI_PARCA_FIT || '').trim()
|
||||||
|
const drop = String(item.DR || '').trim()
|
||||||
|
const kumas = String(item.BIRINCI_PARCA_KUMAS || '').trim()
|
||||||
|
const karisim = String(item.BIRINCI_PARCA_KARISIM || '').trim()
|
||||||
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
||||||
const beden = normalizeSize(item.Beden || '')
|
const beden = normalizeSize(item.Beden || '')
|
||||||
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
||||||
@@ -600,8 +766,14 @@ const level1Groups = computed(() => {
|
|||||||
colorCode,
|
colorCode,
|
||||||
colorDesc,
|
colorDesc,
|
||||||
secondColor,
|
secondColor,
|
||||||
|
kategori,
|
||||||
urunAnaGrubu,
|
urunAnaGrubu,
|
||||||
urunAltGrubu,
|
urunAltGrubu,
|
||||||
|
urunIcerigi,
|
||||||
|
fit,
|
||||||
|
drop,
|
||||||
|
kumas,
|
||||||
|
karisim,
|
||||||
aciklama,
|
aciklama,
|
||||||
sizeTotals: emptySizeTotals(),
|
sizeTotals: emptySizeTotals(),
|
||||||
totalQty: 0,
|
totalQty: 0,
|
||||||
@@ -638,10 +810,12 @@ const level1Groups = computed(() => {
|
|||||||
|
|
||||||
return Array.from(l1Map.values()).map((l1) => ({
|
return Array.from(l1Map.values()).map((l1) => ({
|
||||||
...l1,
|
...l1,
|
||||||
children: Array.from(l1.childrenMap.values()).map((l2) => ({
|
children: Array.from(l1.childrenMap.values())
|
||||||
|
.map((l2) => ({
|
||||||
...l2,
|
...l2,
|
||||||
children: Array.from(l2.childrenMap.values())
|
children: Array.from(l2.childrenMap.values())
|
||||||
}))
|
}))
|
||||||
|
.sort(sortByColorCodeAsc)
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -740,10 +914,65 @@ async function fetchStockByCode() {
|
|||||||
function onLevel2Click(productCode, grp2) {
|
function onLevel2Click(productCode, grp2) {
|
||||||
toggleOpen(grp2.key)
|
toggleOpen(grp2.key)
|
||||||
if (isOpen(grp2.key)) {
|
if (isOpen(grp2.key)) {
|
||||||
void ensureProductImage(productCode, grp2.colorCode)
|
void ensureProductImage(productCode, grp2.colorCode, grp2.secondColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function openProductCard(grp1, grp2) {
|
||||||
|
const productCode = String(grp1?.productCode || '').trim()
|
||||||
|
const colorCode = String(grp2?.colorCode || '').trim()
|
||||||
|
const secondColor = String(grp2?.secondColor || '').trim()
|
||||||
|
const listKey = buildImageKey(productCode, colorCode, secondColor)
|
||||||
|
|
||||||
|
await ensureProductImage(productCode, colorCode, secondColor)
|
||||||
|
const list = Array.isArray(productImageListByCode.value[listKey]) ? productImageListByCode.value[listKey] : []
|
||||||
|
const images = list
|
||||||
|
.map((item) => {
|
||||||
|
const resolved = resolveProductImageUrl(item)
|
||||||
|
return resolved.contentUrl || resolved.publicUrl || ''
|
||||||
|
})
|
||||||
|
.filter((x) => String(x || '').trim() !== '')
|
||||||
|
|
||||||
|
if (!images.length) {
|
||||||
|
const single = getProductImageUrl(productCode, colorCode, secondColor)
|
||||||
|
if (single) images.push(single)
|
||||||
|
}
|
||||||
|
|
||||||
|
productCardImages.value = images
|
||||||
|
productCardSlide.value = 0
|
||||||
|
productCardData.value = {
|
||||||
|
productCode,
|
||||||
|
colorCode,
|
||||||
|
secondColor,
|
||||||
|
kategori: String(grp2?.kategori || '').trim(),
|
||||||
|
urunAnaGrubu: String(grp2?.urunAnaGrubu || '').trim(),
|
||||||
|
urunAltGrubu: String(grp2?.urunAltGrubu || '').trim(),
|
||||||
|
urunIcerigi: String(grp2?.urunIcerigi || '').trim(),
|
||||||
|
fit: String(grp2?.fit || '').trim(),
|
||||||
|
drop: String(grp2?.drop || '').trim(),
|
||||||
|
kumas: String(grp2?.kumas || '').trim(),
|
||||||
|
karisim: String(grp2?.karisim || '').trim(),
|
||||||
|
sizeTotals: grp2?.sizeTotals || {},
|
||||||
|
totalQty: Number(grp2?.totalQty || 0)
|
||||||
|
}
|
||||||
|
productCardDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openProductImageFullscreen(src) {
|
||||||
|
const value = String(src || '').trim()
|
||||||
|
if (!value) return
|
||||||
|
productImageFullscreenSrc.value = value
|
||||||
|
productImageFullscreenZoom.value = 1
|
||||||
|
productImageFullscreenDialog.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleFullscreenImageZoom() {
|
||||||
|
const current = Number(productImageFullscreenZoom.value || 1)
|
||||||
|
if (current < 1.5) productImageFullscreenZoom.value = 1.8
|
||||||
|
else if (current < 2.3) productImageFullscreenZoom.value = 2.6
|
||||||
|
else productImageFullscreenZoom.value = 1
|
||||||
|
}
|
||||||
|
|
||||||
function resetForm() {
|
function resetForm() {
|
||||||
selectedProductCode.value = ''
|
selectedProductCode.value = ''
|
||||||
rawRows.value = []
|
rawRows.value = []
|
||||||
@@ -757,6 +986,13 @@ function resetForm() {
|
|||||||
productImageFallbackByKey.value = {}
|
productImageFallbackByKey.value = {}
|
||||||
productImageContentLoading.value = {}
|
productImageContentLoading.value = {}
|
||||||
productImageListBlockedUntil.value = 0
|
productImageListBlockedUntil.value = 0
|
||||||
|
productCardDialog.value = false
|
||||||
|
productCardData.value = {}
|
||||||
|
productCardImages.value = []
|
||||||
|
productCardSlide.value = 0
|
||||||
|
productImageFullscreenDialog.value = false
|
||||||
|
productImageFullscreenSrc.value = ''
|
||||||
|
productImageFullscreenZoom.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -777,7 +1013,7 @@ onUnmounted(() => {
|
|||||||
--grp-title-w: 44px;
|
--grp-title-w: 44px;
|
||||||
--psq-header-h: 56px;
|
--psq-header-h: 56px;
|
||||||
--psq-col-adet: calc(var(--col-adet) + var(--beden-w));
|
--psq-col-adet: calc(var(--col-adet) + var(--beden-w));
|
||||||
--psq-col-img: 126px;
|
--psq-col-img: 190px;
|
||||||
--psq-l1-lift: 42px;
|
--psq-l1-lift: 42px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -860,8 +1096,8 @@ onUnmounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.order-sub-header.level-2 {
|
.order-sub-header.level-2 {
|
||||||
min-height: 82px !important;
|
min-height: 252px !important;
|
||||||
height: 82px !important;
|
height: 252px !important;
|
||||||
background: #fff9c4 !important;
|
background: #fff9c4 !important;
|
||||||
border-top: 1px solid #d4c79f !important;
|
border-top: 1px solid #d4c79f !important;
|
||||||
border-bottom: 1px solid #d4c79f !important;
|
border-bottom: 1px solid #d4c79f !important;
|
||||||
@@ -1107,15 +1343,17 @@ onUnmounted(() => {
|
|||||||
.order-sub-header.level-2 .sub-image.level2-image {
|
.order-sub-header.level-2 .sub-image.level2-image {
|
||||||
grid-column: 8 / 9;
|
grid-column: 8 / 9;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 6px;
|
gap: 8px;
|
||||||
|
padding: 8px 10px;
|
||||||
border-left: 1px solid #d4c79f;
|
border-left: 1px solid #d4c79f;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image-card {
|
.product-image-card {
|
||||||
width: 110px;
|
width: 162px;
|
||||||
height: 68px;
|
height: 216px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
@@ -1128,6 +1366,7 @@ onUnmounted(() => {
|
|||||||
.product-image {
|
.product-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-image-placeholder {
|
.product-image-placeholder {
|
||||||
@@ -1139,6 +1378,178 @@ onUnmounted(() => {
|
|||||||
background: #f5f6f7;
|
background: #f5f6f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-open-btn {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-dialog {
|
||||||
|
background: #fffef9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-stock {
|
||||||
|
background: #f8f5e7;
|
||||||
|
border: 1px solid #e2d9b6;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-chip {
|
||||||
|
border: 1px solid #d8cca6;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 6px 8px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stock-size-chip .label {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(360px, 1fr) 420px;
|
||||||
|
gap: 12px;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-images {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
min-height: 560px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-carousel {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image-stage {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 560px;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #f7f4e9;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-image-empty {
|
||||||
|
width: 420px;
|
||||||
|
max-width: 100%;
|
||||||
|
height: 560px;
|
||||||
|
border: 1px dashed #cabf9a;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #faf7ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-dialog {
|
||||||
|
background: #f4f0e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-body {
|
||||||
|
height: calc(100vh - 72px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-stage {
|
||||||
|
width: min(96vw, 1400px);
|
||||||
|
height: calc(100vh - 120px);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #efe7cc;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-fullscreen-img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-fields {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
border: 1px solid #e2d9b6;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
height: 560px;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 150px 1fr;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 7px 0;
|
||||||
|
border-bottom: 1px solid #f0ead7;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row .k {
|
||||||
|
color: #5a4f2c;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-row .v {
|
||||||
|
color: #1f1f1f;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.q-btn,
|
||||||
|
.q-icon,
|
||||||
|
.product-image-card,
|
||||||
|
.cursor-pointer {
|
||||||
|
cursor: pointer !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.product-card-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-images,
|
||||||
|
.product-card-fields {
|
||||||
|
grid-column: auto;
|
||||||
|
grid-row: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-card-fields {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.order-sub-header.level-2 .sub-right .top-total {
|
.order-sub-header.level-2 .sub-right .top-total {
|
||||||
grid-column: 1 / 2;
|
grid-column: 1 / 2;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user