Merge remote-tracking branch 'origin/master'
This commit is contained in:
12
svc/main.go
12
svc/main.go
@@ -561,6 +561,18 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
|
|||||||
wrapV3(http.HandlerFunc(routes.GetProductStockQueryHandler)),
|
wrapV3(http.HandlerFunc(routes.GetProductStockQueryHandler)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/product-stock-attribute-options", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductStockAttributeOptionsHandler)),
|
||||||
|
)
|
||||||
|
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/product-stock-query-by-attributes", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductStockQueryByAttributesHandler)),
|
||||||
|
)
|
||||||
|
|
||||||
bindV3(r, pgDB,
|
bindV3(r, pgDB,
|
||||||
"/api/product-images", "GET",
|
"/api/product-images", "GET",
|
||||||
"order", "view",
|
"order", "view",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package queries
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"bssapp-backend/models"
|
"bssapp-backend/models"
|
||||||
)
|
)
|
||||||
@@ -156,7 +157,16 @@ FROM dbo.prItemVariant WITH (UPDLOCK, HOLDLOCK)
|
|||||||
}
|
}
|
||||||
|
|
||||||
var inserted int64
|
var inserted int64
|
||||||
|
ensuredItems := make(map[string]struct{}, len(missing))
|
||||||
for i, v := range missing {
|
for i, v := range missing {
|
||||||
|
itemKey := strconv.FormatInt(int64(v.ItemTypeCode), 10) + "|" + v.ItemCode
|
||||||
|
if _, ok := ensuredItems[itemKey]; !ok {
|
||||||
|
if err := ensureCdItemTx(tx, v.ItemTypeCode, v.ItemCode, username); err != nil {
|
||||||
|
return inserted, err
|
||||||
|
}
|
||||||
|
ensuredItems[itemKey] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
plu := basePlu + int64(i) + 1
|
plu := basePlu + int64(i) + 1
|
||||||
res, err := tx.Exec(`
|
res, err := tx.Exec(`
|
||||||
IF NOT EXISTS (
|
IF NOT EXISTS (
|
||||||
@@ -197,6 +207,93 @@ VALUES (
|
|||||||
return inserted, nil
|
return inserted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ensureCdItemTx(tx *sql.Tx, itemTypeCode int16, itemCode string, username string) error {
|
||||||
|
_, err := tx.Exec(`
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.cdItem
|
||||||
|
WHERE ItemTypeCode = @p1
|
||||||
|
AND ItemCode = @p2
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
;WITH Template AS (
|
||||||
|
SELECT TOP 1
|
||||||
|
ItemDimTypeCode, ProductTypeCode, ProductHierarchyID,
|
||||||
|
UnitOfMeasureCode1, UnitOfMeasureCode2, UnitConvertRate, UnitConvertRateNotFixed,
|
||||||
|
UseInternet, UsePOS, UseStore, EnablePartnerCompanies, UseManufacturing, UseSerialNumber,
|
||||||
|
GenerateOpticalDataMatrixCode, ByWeight, SupplyPeriod, GuaranteePeriod, ShelfLife, OrderLeadTime,
|
||||||
|
ItemAccountGrCode, ItemTaxGrCode, ItemPaymentPlanGrCode, ItemDiscountGrCode, ItemVendorGrCode,
|
||||||
|
PromotionGroupCode, PromotionGroupCode2, ProductCollectionGrCode, StorePriceLevelCode, PerceptionOfFashionCode,
|
||||||
|
CommercialRoleCode, StoreCapacityLevelCode, CustomsTariffNumberCode, IsFixedExpense, BOMEntityCode, CompanyCode,
|
||||||
|
IsBlocked, IsLocked, LockedDate, IsSalesOrderClosed, IsPurchaseOrderClosed, UseRoll, UseBatch,
|
||||||
|
MaxCreditCardInstallmentCount, GenerateSerialNumber, IsSubsequentDeliveryForR, IsSubsequentDeliveryForRI,
|
||||||
|
IGACommissionGroup, UniFreeCommissionGroup, CustomsProductGroupCode, IsUTSDeclaratedItem, IsStoreOrderClosed
|
||||||
|
FROM dbo.cdItem WITH (UPDLOCK, HOLDLOCK)
|
||||||
|
WHERE ItemTypeCode = @p1
|
||||||
|
AND ItemCode LIKE 'U%'
|
||||||
|
ORDER BY CreatedDate DESC
|
||||||
|
)
|
||||||
|
INSERT INTO dbo.cdItem (
|
||||||
|
ItemTypeCode, ItemCode,
|
||||||
|
ItemDimTypeCode, ProductTypeCode, ProductHierarchyID,
|
||||||
|
UnitOfMeasureCode1, UnitOfMeasureCode2, UnitConvertRate, UnitConvertRateNotFixed,
|
||||||
|
UseInternet, UsePOS, UseStore, EnablePartnerCompanies, UseManufacturing, UseSerialNumber,
|
||||||
|
GenerateOpticalDataMatrixCode, ByWeight, SupplyPeriod, GuaranteePeriod, ShelfLife, OrderLeadTime,
|
||||||
|
ItemAccountGrCode, ItemTaxGrCode, ItemPaymentPlanGrCode, ItemDiscountGrCode, ItemVendorGrCode,
|
||||||
|
PromotionGroupCode, PromotionGroupCode2, ProductCollectionGrCode, StorePriceLevelCode, PerceptionOfFashionCode,
|
||||||
|
CommercialRoleCode, StoreCapacityLevelCode, CustomsTariffNumberCode, IsFixedExpense, BOMEntityCode, CompanyCode,
|
||||||
|
IsBlocked, IsLocked, LockedDate, IsSalesOrderClosed, IsPurchaseOrderClosed,
|
||||||
|
CreatedUserName, CreatedDate, LastUpdatedUserName, LastUpdatedDate, RowGuid,
|
||||||
|
UseRoll, UseBatch, MaxCreditCardInstallmentCount, GenerateSerialNumber,
|
||||||
|
IsSubsequentDeliveryForR, IsSubsequentDeliveryForRI,
|
||||||
|
IGACommissionGroup, UniFreeCommissionGroup, CustomsProductGroupCode, IsUTSDeclaratedItem, IsStoreOrderClosed
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
@p1, @p2,
|
||||||
|
t.ItemDimTypeCode, t.ProductTypeCode, t.ProductHierarchyID,
|
||||||
|
t.UnitOfMeasureCode1, t.UnitOfMeasureCode2, t.UnitConvertRate, t.UnitConvertRateNotFixed,
|
||||||
|
t.UseInternet, t.UsePOS, t.UseStore, t.EnablePartnerCompanies, t.UseManufacturing, t.UseSerialNumber,
|
||||||
|
t.GenerateOpticalDataMatrixCode, t.ByWeight, t.SupplyPeriod, t.GuaranteePeriod, t.ShelfLife, t.OrderLeadTime,
|
||||||
|
t.ItemAccountGrCode, t.ItemTaxGrCode, t.ItemPaymentPlanGrCode, t.ItemDiscountGrCode, t.ItemVendorGrCode,
|
||||||
|
t.PromotionGroupCode, t.PromotionGroupCode2, t.ProductCollectionGrCode, t.StorePriceLevelCode, t.PerceptionOfFashionCode,
|
||||||
|
t.CommercialRoleCode, t.StoreCapacityLevelCode, t.CustomsTariffNumberCode, t.IsFixedExpense, t.BOMEntityCode, t.CompanyCode,
|
||||||
|
t.IsBlocked, t.IsLocked, t.LockedDate, t.IsSalesOrderClosed, t.IsPurchaseOrderClosed,
|
||||||
|
@p3, GETDATE(), @p3, GETDATE(), NEWID(),
|
||||||
|
t.UseRoll, t.UseBatch, t.MaxCreditCardInstallmentCount, t.GenerateSerialNumber,
|
||||||
|
t.IsSubsequentDeliveryForR, t.IsSubsequentDeliveryForRI,
|
||||||
|
t.IGACommissionGroup, t.UniFreeCommissionGroup, t.CustomsProductGroupCode, t.IsUTSDeclaratedItem, t.IsStoreOrderClosed
|
||||||
|
FROM Template t;
|
||||||
|
|
||||||
|
IF @@ROWCOUNT = 0
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO dbo.cdItem (
|
||||||
|
ItemTypeCode, ItemCode,
|
||||||
|
ItemDimTypeCode, ProductTypeCode, ProductHierarchyID,
|
||||||
|
UnitOfMeasureCode1, UnitConvertRate, UnitConvertRateNotFixed,
|
||||||
|
UseInternet, UsePOS, UseStore, EnablePartnerCompanies, UseManufacturing, UseSerialNumber,
|
||||||
|
GenerateOpticalDataMatrixCode, ByWeight, SupplyPeriod, GuaranteePeriod, ShelfLife, OrderLeadTime,
|
||||||
|
IsFixedExpense, IsBlocked, IsLocked, LockedDate, IsSalesOrderClosed, IsPurchaseOrderClosed,
|
||||||
|
CreatedUserName, CreatedDate, LastUpdatedUserName, LastUpdatedDate, RowGuid,
|
||||||
|
UseRoll, UseBatch, MaxCreditCardInstallmentCount, GenerateSerialNumber,
|
||||||
|
IsSubsequentDeliveryForR, IsSubsequentDeliveryForRI, IsUTSDeclaratedItem, IsStoreOrderClosed
|
||||||
|
)
|
||||||
|
VALUES (
|
||||||
|
@p1, @p2,
|
||||||
|
2, 1, 2,
|
||||||
|
'AD', 0, 0,
|
||||||
|
0, 1, 1, 0, 1, 0,
|
||||||
|
0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, '1900-01-01', 0, 0,
|
||||||
|
@p3, GETDATE(), @p3, GETDATE(), NEWID(),
|
||||||
|
0, 0, 12, 0,
|
||||||
|
0, 0, 0, 0
|
||||||
|
);
|
||||||
|
END
|
||||||
|
END
|
||||||
|
`, itemTypeCode, itemCode, username)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderProductionUpdateLine, username string) (int64, error) {
|
func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderProductionUpdateLine, username string) (int64, error) {
|
||||||
var updated int64
|
var updated int64
|
||||||
for _, line := range lines {
|
for _, line := range lines {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package queries
|
package queries
|
||||||
|
|
||||||
// GetProductStockQuery:
|
// GetProductStockQuery:
|
||||||
// Ürün kodu bazlı, optimize stok + attribute sorgusu.
|
// Urun kodu bazli, STOCK/PICK/RESERVE/DISP ayrik CTE ile optimize sorgu.
|
||||||
const GetProductStockQuery = `
|
const GetProductStockQuery = `
|
||||||
DECLARE @ProductCode NVARCHAR(50) = @p1;
|
DECLARE @ProductCode NVARCHAR(50) = @p1;
|
||||||
|
|
||||||
;WITH INV AS
|
;WITH STOCK AS
|
||||||
(
|
(
|
||||||
SELECT
|
SELECT
|
||||||
CompanyCode,
|
CompanyCode,
|
||||||
@@ -19,49 +19,7 @@ DECLARE @ProductCode NVARCHAR(50) = @p1;
|
|||||||
ItemDim1Code,
|
ItemDim1Code,
|
||||||
ItemDim2Code,
|
ItemDim2Code,
|
||||||
ItemDim3Code,
|
ItemDim3Code,
|
||||||
SUM(PickingQty1) AS PickingQty1,
|
SUM(In_Qty1 - Out_Qty1) AS InventoryQty1
|
||||||
SUM(ReserveQty1) AS ReserveQty1,
|
|
||||||
SUM(DispOrderQty1) AS DispOrderQty1,
|
|
||||||
SUM(InventoryQty1) AS InventoryQty1
|
|
||||||
FROM
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
|
||||||
Qty1 AS PickingQty1, 0 AS ReserveQty1, 0 AS DispOrderQty1, 0 AS InventoryQty1
|
|
||||||
FROM PickingStates
|
|
||||||
WHERE ItemTypeCode = 1
|
|
||||||
AND ItemCode = @ProductCode
|
|
||||||
AND LEN(ItemCode) = 13
|
|
||||||
AND LEN(@ProductCode) = 13
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
SELECT
|
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
|
||||||
0, Qty1, 0, 0
|
|
||||||
FROM ReserveStates
|
|
||||||
WHERE ItemTypeCode = 1
|
|
||||||
AND ItemCode = @ProductCode
|
|
||||||
AND LEN(ItemCode) = 13
|
|
||||||
AND LEN(@ProductCode) = 13
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
SELECT
|
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
|
||||||
0, 0, Qty1, 0
|
|
||||||
FROM DispOrderStates
|
|
||||||
WHERE ItemTypeCode = 1
|
|
||||||
AND ItemCode = @ProductCode
|
|
||||||
AND LEN(ItemCode) = 13
|
|
||||||
AND LEN(@ProductCode) = 13
|
|
||||||
|
|
||||||
UNION ALL
|
|
||||||
SELECT
|
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
|
||||||
0, 0, 0, SUM(In_Qty1 - Out_Qty1)
|
|
||||||
FROM trStock WITH(NOLOCK)
|
FROM trStock WITH(NOLOCK)
|
||||||
WHERE ItemTypeCode = 1
|
WHERE ItemTypeCode = 1
|
||||||
AND ItemCode = @ProductCode
|
AND ItemCode = @ProductCode
|
||||||
@@ -69,130 +27,208 @@ DECLARE @ProductCode NVARCHAR(50) = @p1;
|
|||||||
AND LEN(@ProductCode) = 13
|
AND LEN(@ProductCode) = 13
|
||||||
GROUP BY
|
GROUP BY
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
) X
|
ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||||
GROUP BY
|
),
|
||||||
|
|
||||||
|
PICK AS
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
),
|
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||||
Attr AS
|
SUM(Qty1) AS PickingQty1
|
||||||
(
|
FROM PickingStates
|
||||||
SELECT TOP 1
|
|
||||||
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
|
|
||||||
FROM ProductFilterWithDescription('TR')
|
|
||||||
WHERE ProductCode = @ProductCode
|
|
||||||
AND LEN(ProductCode) = 13
|
|
||||||
AND LEN(@ProductCode) = 13
|
|
||||||
),
|
|
||||||
Price AS
|
|
||||||
(
|
|
||||||
SELECT TOP 1
|
|
||||||
Price
|
|
||||||
FROM prItemBasePrice WITH (NOLOCK)
|
|
||||||
WHERE ItemTypeCode = 1
|
WHERE ItemTypeCode = 1
|
||||||
AND ItemCode = @ProductCode
|
AND ItemCode = @ProductCode
|
||||||
AND LEN(ItemCode) = 13
|
AND LEN(ItemCode) = 13
|
||||||
AND LEN(@ProductCode) = 13
|
AND LEN(@ProductCode) = 13
|
||||||
ORDER BY PriceDate DESC
|
GROUP BY
|
||||||
)
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
|
ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||||
|
),
|
||||||
|
|
||||||
|
RESERVE AS
|
||||||
|
(
|
||||||
SELECT
|
SELECT
|
||||||
I.WarehouseCode AS Depo_Kodu,
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
W.WarehouseDescription AS Depo_Adi,
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
IT.ItemTypeDescription AS InventoryType,
|
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||||
I.ItemCode AS Urun_Kodu,
|
SUM(Qty1) AS ReserveQty1
|
||||||
A.ProductDescription AS Madde_Aciklamasi,
|
FROM ReserveStates
|
||||||
I.ColorCode AS Renk_Kodu,
|
WHERE ItemTypeCode = 1
|
||||||
C.ColorDescription AS Renk_Aciklamasi,
|
AND ItemCode = @ProductCode
|
||||||
I.ItemDim1Code AS Beden,
|
AND LEN(ItemCode) = 13
|
||||||
I.ItemDim2Code AS Yaka,
|
|
||||||
ROUND(I.InventoryQty1 - I.PickingQty1 - I.ReserveQty1 - I.DispOrderQty1, U.RoundDigit) AS Kullanilabilir_Envanter,
|
|
||||||
A.ProductAtt01Desc AS URUN_ANA_GRUBU,
|
|
||||||
A.ProductAtt02Desc AS URUN_ALT_GRUBU,
|
|
||||||
A.ProductAtt10Desc AS MARKA,
|
|
||||||
A.ProductAtt11Desc AS DR,
|
|
||||||
A.ProductAtt21Desc AS KALIP,
|
|
||||||
A.ProductAtt22Desc AS IKINCI_PARCA_KALIP,
|
|
||||||
A.ProductAtt23Desc AS PACA_GENISLIGI,
|
|
||||||
A.ProductAtt24Desc AS UCUNCU_PARCA_KALIP,
|
|
||||||
A.ProductAtt25Desc AS UCUNCU_PARCA_MODEL,
|
|
||||||
A.ProductAtt26Desc AS BIRINCI_PARCA_KUMAS,
|
|
||||||
A.ProductAtt27Desc AS IKINCI_PARCA_KUMAS,
|
|
||||||
A.ProductAtt28Desc AS UCUNCU_PARCA_KUMAS,
|
|
||||||
A.ProductAtt29Desc AS BIRINCI_PARCA_KARISIM,
|
|
||||||
A.ProductAtt30Desc AS IKINCI_PARCA_KARISIM,
|
|
||||||
A.ProductAtt31Desc AS UCUNCU_PARCA_KARISIM,
|
|
||||||
A.ProductAtt32Desc AS YAKA_TIPI,
|
|
||||||
A.ProductAtt33Desc AS DUGME,
|
|
||||||
A.ProductAtt34Desc AS YIRTMAC,
|
|
||||||
A.ProductAtt35Desc AS SEZON_YILI,
|
|
||||||
A.ProductAtt36Desc AS MEVSIM,
|
|
||||||
A.ProductAtt37Desc AS TABAN,
|
|
||||||
A.ProductAtt38Desc AS BIRINCI_PARCA_FIT,
|
|
||||||
A.ProductAtt39Desc AS IKINCI_PARCA_FIT,
|
|
||||||
A.ProductAtt40Desc AS BOS2,
|
|
||||||
A.ProductAtt41Desc AS KISA_KAR,
|
|
||||||
A.ProductAtt42Desc AS SERI_FASON,
|
|
||||||
A.ProductAtt43Desc AS STOK_GIRIS_YONTEMI,
|
|
||||||
A.ProductAtt44Desc AS YETISKIN_GARSON,
|
|
||||||
A.ProductAtt45Desc AS ASKILI_YAN,
|
|
||||||
A.ProductAtt46Desc AS BOS3,
|
|
||||||
P.Price AS Fiyat
|
|
||||||
FROM INV I
|
|
||||||
JOIN cdItem CI WITH (NOLOCK)
|
|
||||||
ON CI.ItemTypeCode = I.ItemTypeCode
|
|
||||||
AND CI.ItemCode = I.ItemCode
|
|
||||||
LEFT JOIN cdUnitOfMeasure U WITH (NOLOCK)
|
|
||||||
ON U.UnitOfMeasureCode = CI.UnitOfMeasureCode1
|
|
||||||
LEFT JOIN cdWarehouseDesc W WITH (NOLOCK)
|
|
||||||
ON W.WarehouseCode = I.WarehouseCode
|
|
||||||
AND W.LangCode = 'TR'
|
|
||||||
LEFT JOIN bsItemTypeDesc IT WITH (NOLOCK)
|
|
||||||
ON IT.ItemTypeCode = I.ItemTypeCode
|
|
||||||
AND IT.LangCode = 'TR'
|
|
||||||
LEFT JOIN cdColorDesc C WITH (NOLOCK)
|
|
||||||
ON C.ColorCode = I.ColorCode
|
|
||||||
AND C.LangCode = 'TR'
|
|
||||||
CROSS JOIN Attr A
|
|
||||||
OUTER APPLY (SELECT TOP 1 Price FROM Price) P
|
|
||||||
WHERE
|
|
||||||
I.ItemTypeCode = 1
|
|
||||||
AND I.ItemCode = @ProductCode
|
|
||||||
AND LEN(I.ItemCode) = 13
|
|
||||||
AND LEN(@ProductCode) = 13
|
AND LEN(@ProductCode) = 13
|
||||||
AND (I.InventoryQty1 - I.PickingQty1 - I.ReserveQty1 - I.DispOrderQty1) > 0
|
GROUP BY
|
||||||
AND CI.IsBlocked = 0
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
AND I.WarehouseCode IN
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
|
ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||||
|
),
|
||||||
|
|
||||||
|
DISP AS
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
|
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||||
|
SUM(Qty1) AS DispOrderQty1
|
||||||
|
FROM DispOrderStates
|
||||||
|
WHERE ItemTypeCode = 1
|
||||||
|
AND ItemCode = @ProductCode
|
||||||
|
AND LEN(ItemCode) = 13
|
||||||
|
AND LEN(@ProductCode) = 13
|
||||||
|
GROUP BY
|
||||||
|
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||||
|
ItemTypeCode, ItemCode, ColorCode,
|
||||||
|
ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||||
|
)
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
S.WarehouseCode AS Depo_Kodu,
|
||||||
|
W.WarehouseDescription AS Depo_Adi,
|
||||||
|
|
||||||
|
bsItemTypeDesc.ItemTypeDescription AS InventoryType,
|
||||||
|
|
||||||
|
S.ItemCode AS Urun_Kodu,
|
||||||
|
P.ProductDescription AS Madde_Aciklamasi,
|
||||||
|
|
||||||
|
S.ColorCode AS Renk_Kodu,
|
||||||
|
C.ColorDescription AS Renk_Aciklamasi,
|
||||||
|
|
||||||
|
S.ItemDim1Code AS Beden,
|
||||||
|
S.ItemDim2Code AS Yaka,
|
||||||
|
|
||||||
|
ROUND(
|
||||||
|
S.InventoryQty1
|
||||||
|
- ISNULL(PK.PickingQty1,0)
|
||||||
|
- ISNULL(RS.ReserveQty1,0)
|
||||||
|
- ISNULL(DP.DispOrderQty1,0),
|
||||||
|
cdUnitOfMeasure.RoundDigit
|
||||||
|
) AS Kullanilabilir_Envanter,
|
||||||
|
|
||||||
|
P.ProductAtt01Desc AS URUN_ANA_GRUBU,
|
||||||
|
P.ProductAtt02Desc AS URUN_ALT_GRUBU,
|
||||||
|
P.ProductAtt10Desc AS MARKA,
|
||||||
|
P.ProductAtt11Desc AS DR,
|
||||||
|
P.ProductAtt21Desc AS KALIP,
|
||||||
|
P.ProductAtt22Desc AS IKINCI_PARCA_KALIP,
|
||||||
|
P.ProductAtt23Desc AS PACA_GENISLIGI,
|
||||||
|
P.ProductAtt24Desc AS UCUNCU_PARCA_KALIP,
|
||||||
|
P.ProductAtt25Desc AS UCUNCU_PARCA_MODEL,
|
||||||
|
P.ProductAtt26Desc AS BIRINCI_PARCA_KUMAS,
|
||||||
|
P.ProductAtt27Desc AS IKINCI_PARCA_KUMAS,
|
||||||
|
P.ProductAtt28Desc AS UCUNCU_PARCA_KUMAS,
|
||||||
|
P.ProductAtt29Desc AS BIRINCI_PARCA_KARISIM,
|
||||||
|
P.ProductAtt30Desc AS IKINCI_PARCA_KARISIM,
|
||||||
|
P.ProductAtt31Desc AS UCUNCU_PARCA_KARISIM,
|
||||||
|
P.ProductAtt32Desc AS YAKA_TIPI,
|
||||||
|
P.ProductAtt33Desc AS DUGME,
|
||||||
|
P.ProductAtt34Desc AS YIRTMAC,
|
||||||
|
P.ProductAtt35Desc AS SEZON_YILI,
|
||||||
|
P.ProductAtt36Desc AS MEVSIM,
|
||||||
|
P.ProductAtt37Desc AS TABAN,
|
||||||
|
P.ProductAtt38Desc AS BIRINCI_PARCA_FIT,
|
||||||
|
P.ProductAtt39Desc AS IKINCI_PARCA_FIT,
|
||||||
|
P.ProductAtt40Desc AS BOS2,
|
||||||
|
P.ProductAtt41Desc AS KISA_KAR,
|
||||||
|
P.ProductAtt42Desc AS SERI_FASON,
|
||||||
|
P.ProductAtt43Desc AS STOK_GIRIS_YONTEMI,
|
||||||
|
P.ProductAtt44Desc AS YETISKIN_GARSON,
|
||||||
|
P.ProductAtt45Desc AS ASKILI_YAN,
|
||||||
|
P.ProductAtt46Desc AS BOS3,
|
||||||
|
|
||||||
|
prFilteredBasePrice.Price AS Fiyat
|
||||||
|
|
||||||
|
FROM STOCK S
|
||||||
|
|
||||||
|
LEFT JOIN PICK PK
|
||||||
|
ON PK.CompanyCode=S.CompanyCode
|
||||||
|
AND PK.OfficeCode=S.OfficeCode
|
||||||
|
AND PK.StoreTypeCode=S.StoreTypeCode
|
||||||
|
AND PK.StoreCode=S.StoreCode
|
||||||
|
AND PK.WarehouseCode=S.WarehouseCode
|
||||||
|
AND PK.ItemTypeCode=S.ItemTypeCode
|
||||||
|
AND PK.ItemCode=S.ItemCode
|
||||||
|
AND PK.ColorCode=S.ColorCode
|
||||||
|
AND PK.ItemDim1Code=S.ItemDim1Code
|
||||||
|
AND PK.ItemDim2Code=S.ItemDim2Code
|
||||||
|
AND PK.ItemDim3Code=S.ItemDim3Code
|
||||||
|
|
||||||
|
LEFT JOIN RESERVE RS
|
||||||
|
ON RS.CompanyCode=S.CompanyCode
|
||||||
|
AND RS.OfficeCode=S.OfficeCode
|
||||||
|
AND RS.StoreTypeCode=S.StoreTypeCode
|
||||||
|
AND RS.StoreCode=S.StoreCode
|
||||||
|
AND RS.WarehouseCode=S.WarehouseCode
|
||||||
|
AND RS.ItemTypeCode=S.ItemTypeCode
|
||||||
|
AND RS.ItemCode=S.ItemCode
|
||||||
|
AND RS.ColorCode=S.ColorCode
|
||||||
|
AND RS.ItemDim1Code=S.ItemDim1Code
|
||||||
|
AND RS.ItemDim2Code=S.ItemDim2Code
|
||||||
|
AND RS.ItemDim3Code=S.ItemDim3Code
|
||||||
|
|
||||||
|
LEFT JOIN DISP DP
|
||||||
|
ON DP.CompanyCode=S.CompanyCode
|
||||||
|
AND DP.OfficeCode=S.OfficeCode
|
||||||
|
AND DP.StoreTypeCode=S.StoreTypeCode
|
||||||
|
AND DP.StoreCode=S.StoreCode
|
||||||
|
AND DP.WarehouseCode=S.WarehouseCode
|
||||||
|
AND DP.ItemTypeCode=S.ItemTypeCode
|
||||||
|
AND DP.ItemCode=S.ItemCode
|
||||||
|
AND DP.ColorCode=S.ColorCode
|
||||||
|
AND DP.ItemDim1Code=S.ItemDim1Code
|
||||||
|
AND DP.ItemDim2Code=S.ItemDim2Code
|
||||||
|
AND DP.ItemDim3Code=S.ItemDim3Code
|
||||||
|
|
||||||
|
JOIN cdItem WITH(NOLOCK)
|
||||||
|
ON S.ItemCode = cdItem.ItemCode
|
||||||
|
AND S.ItemTypeCode = cdItem.ItemTypeCode
|
||||||
|
|
||||||
|
LEFT JOIN cdUnitOfMeasure WITH(NOLOCK)
|
||||||
|
ON cdItem.UnitOfMeasureCode1 = cdUnitOfMeasure.UnitOfMeasureCode
|
||||||
|
|
||||||
|
LEFT JOIN ProductFilterWithDescription('TR') P
|
||||||
|
ON P.ProductCode = S.ItemCode
|
||||||
|
|
||||||
|
LEFT JOIN bsItemTypeDesc WITH(NOLOCK)
|
||||||
|
ON bsItemTypeDesc.ItemTypeCode = S.ItemTypeCode
|
||||||
|
AND bsItemTypeDesc.LangCode='TR'
|
||||||
|
|
||||||
|
LEFT JOIN cdWarehouseDesc W WITH(NOLOCK)
|
||||||
|
ON W.WarehouseCode = S.WarehouseCode
|
||||||
|
AND W.LangCode='TR'
|
||||||
|
|
||||||
|
LEFT JOIN cdColorDesc C WITH(NOLOCK)
|
||||||
|
ON C.ColorCode = S.ColorCode
|
||||||
|
AND C.LangCode='TR'
|
||||||
|
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT
|
||||||
|
ItemCode,
|
||||||
|
ItemTypeCode,
|
||||||
|
Price,
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY ItemCode, ItemTypeCode ORDER BY PriceDate DESC) AS RowNum
|
||||||
|
FROM prItemBasePrice WITH(NOLOCK)
|
||||||
|
) prFilteredBasePrice
|
||||||
|
ON prFilteredBasePrice.ItemCode = S.ItemCode
|
||||||
|
AND prFilteredBasePrice.ItemTypeCode = S.ItemTypeCode
|
||||||
|
AND prFilteredBasePrice.RowNum = 1
|
||||||
|
|
||||||
|
WHERE
|
||||||
|
S.ItemTypeCode IN (1)
|
||||||
|
AND S.ItemCode = @ProductCode
|
||||||
|
AND LEN(S.ItemCode) = 13
|
||||||
|
AND LEN(@ProductCode) = 13
|
||||||
|
AND (
|
||||||
|
S.InventoryQty1
|
||||||
|
- ISNULL(PK.PickingQty1,0)
|
||||||
|
- ISNULL(RS.ReserveQty1,0)
|
||||||
|
- ISNULL(DP.DispOrderQty1,0)
|
||||||
|
) > 0
|
||||||
|
AND cdItem.IsBlocked = 0
|
||||||
|
AND S.WarehouseCode IN
|
||||||
(
|
(
|
||||||
'1-0-14','1-0-10','1-0-8','1-2-5','1-2-4','1-0-12','100','1-0-28',
|
'1-0-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-24','1-2-6','1-1-14','1-0-2','1-0-52','1-1-2','1-0-21','1-1-3',
|
||||||
|
|||||||
239
svc/queries/productstockquery_by_attributes.go
Normal file
239
svc/queries/productstockquery_by_attributes.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
// GetProductStockAttributeOptionsQuery:
|
||||||
|
// Urun ozellik filtre secenekleri (distinct aciklamalar).
|
||||||
|
const GetProductStockAttributeOptionsQuery = `
|
||||||
|
WITH PF AS
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
LTRIM(RTRIM(ProductAtt01Desc)) AS ProductAtt01Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt02Desc)) AS ProductAtt02Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt10Desc)) AS ProductAtt10Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt11Desc)) AS ProductAtt11Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt21Desc)) AS ProductAtt21Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt35Desc)) AS ProductAtt35Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt36Desc)) AS ProductAtt36Desc,
|
||||||
|
LTRIM(RTRIM(ProductAtt44Desc)) AS ProductAtt44Desc
|
||||||
|
FROM ProductFilterWithDescription('TR')
|
||||||
|
WHERE LEN(ProductCode) = 13
|
||||||
|
)
|
||||||
|
SELECT 'att01' AS FieldName, ProductAtt01Desc AS FieldValue FROM PF WHERE ProductAtt01Desc <> '' GROUP BY ProductAtt01Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att02', ProductAtt02Desc FROM PF WHERE ProductAtt02Desc <> '' GROUP BY ProductAtt02Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att10', ProductAtt10Desc FROM PF WHERE ProductAtt10Desc <> '' GROUP BY ProductAtt10Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att11', ProductAtt11Desc FROM PF WHERE ProductAtt11Desc <> '' GROUP BY ProductAtt11Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att21', ProductAtt21Desc FROM PF WHERE ProductAtt21Desc <> '' GROUP BY ProductAtt21Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att35', ProductAtt35Desc FROM PF WHERE ProductAtt35Desc <> '' GROUP BY ProductAtt35Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att36', ProductAtt36Desc FROM PF WHERE ProductAtt36Desc <> '' GROUP BY ProductAtt36Desc
|
||||||
|
UNION ALL
|
||||||
|
SELECT 'att44', ProductAtt44Desc FROM PF WHERE ProductAtt44Desc <> '' GROUP BY ProductAtt44Desc;
|
||||||
|
`
|
||||||
|
|
||||||
|
// GetProductStockQueryByAttributes:
|
||||||
|
// Urun ozelliklerine gore stok detay sorgusu.
|
||||||
|
const GetProductStockQueryByAttributes = `
|
||||||
|
DECLARE @Att01 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p1)), '');
|
||||||
|
DECLARE @Att02 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p2)), '');
|
||||||
|
DECLARE @Att10 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p3)), '');
|
||||||
|
DECLARE @Att11 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p4)), '');
|
||||||
|
DECLARE @Att21 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p5)), '');
|
||||||
|
DECLARE @Att35 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p6)), '');
|
||||||
|
DECLARE @Att36 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p7)), '');
|
||||||
|
DECLARE @Att44 NVARCHAR(100) = NULLIF(LTRIM(RTRIM(@p8)), '');
|
||||||
|
|
||||||
|
;WITH AttrFiltered AS
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
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
|
||||||
|
FROM ProductFilterWithDescription('TR')
|
||||||
|
WHERE LEN(ProductCode) = 13
|
||||||
|
AND (@Att01 IS NULL OR ProductAtt01Desc = @Att01)
|
||||||
|
AND (@Att02 IS NULL OR ProductAtt02Desc = @Att02)
|
||||||
|
AND (@Att10 IS NULL OR ProductAtt10Desc = @Att10)
|
||||||
|
AND (@Att11 IS NULL OR ProductAtt11Desc = @Att11)
|
||||||
|
AND (@Att21 IS NULL OR ProductAtt21Desc = @Att21)
|
||||||
|
AND (@Att35 IS NULL OR ProductAtt35Desc = @Att35)
|
||||||
|
AND (@Att36 IS NULL OR ProductAtt36Desc = @Att36)
|
||||||
|
AND (@Att44 IS NULL OR ProductAtt44Desc = @Att44)
|
||||||
|
),
|
||||||
|
INV AS
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
X.CompanyCode,
|
||||||
|
X.OfficeCode,
|
||||||
|
X.StoreTypeCode,
|
||||||
|
X.StoreCode,
|
||||||
|
X.WarehouseCode,
|
||||||
|
X.ItemTypeCode,
|
||||||
|
X.ItemCode,
|
||||||
|
X.ColorCode,
|
||||||
|
X.ItemDim1Code,
|
||||||
|
X.ItemDim2Code,
|
||||||
|
X.ItemDim3Code,
|
||||||
|
SUM(X.PickingQty1) AS PickingQty1,
|
||||||
|
SUM(X.ReserveQty1) AS ReserveQty1,
|
||||||
|
SUM(X.DispOrderQty1) AS DispOrderQty1,
|
||||||
|
SUM(X.InventoryQty1) AS InventoryQty1
|
||||||
|
FROM
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
P.CompanyCode, P.OfficeCode, P.StoreTypeCode, P.StoreCode, P.WarehouseCode,
|
||||||
|
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
|
||||||
|
FROM PickingStates P
|
||||||
|
INNER JOIN AttrFiltered AF ON AF.ProductCode = P.ItemCode
|
||||||
|
WHERE P.ItemTypeCode = 1
|
||||||
|
AND LEN(P.ItemCode) = 13
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
R.CompanyCode, R.OfficeCode, R.StoreTypeCode, R.StoreCode, R.WarehouseCode,
|
||||||
|
R.ItemTypeCode, R.ItemCode, R.ColorCode, R.ItemDim1Code, R.ItemDim2Code, R.ItemDim3Code,
|
||||||
|
0, R.Qty1, 0, 0
|
||||||
|
FROM ReserveStates R
|
||||||
|
INNER JOIN AttrFiltered AF ON AF.ProductCode = R.ItemCode
|
||||||
|
WHERE R.ItemTypeCode = 1
|
||||||
|
AND LEN(R.ItemCode) = 13
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
D.CompanyCode, D.OfficeCode, D.StoreTypeCode, D.StoreCode, D.WarehouseCode,
|
||||||
|
D.ItemTypeCode, D.ItemCode, D.ColorCode, D.ItemDim1Code, D.ItemDim2Code, D.ItemDim3Code,
|
||||||
|
0, 0, D.Qty1, 0
|
||||||
|
FROM DispOrderStates D
|
||||||
|
INNER JOIN AttrFiltered AF ON AF.ProductCode = D.ItemCode
|
||||||
|
WHERE D.ItemTypeCode = 1
|
||||||
|
AND LEN(D.ItemCode) = 13
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode,
|
||||||
|
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code,
|
||||||
|
0, 0, 0, SUM(T.In_Qty1 - T.Out_Qty1)
|
||||||
|
FROM trStock T WITH (NOLOCK)
|
||||||
|
INNER JOIN AttrFiltered AF ON AF.ProductCode = T.ItemCode
|
||||||
|
WHERE T.ItemTypeCode = 1
|
||||||
|
AND LEN(T.ItemCode) = 13
|
||||||
|
GROUP BY
|
||||||
|
T.CompanyCode, T.OfficeCode, T.StoreTypeCode, T.StoreCode, T.WarehouseCode,
|
||||||
|
T.ItemTypeCode, T.ItemCode, T.ColorCode, T.ItemDim1Code, T.ItemDim2Code, T.ItemDim3Code
|
||||||
|
) X
|
||||||
|
GROUP BY
|
||||||
|
X.CompanyCode, X.OfficeCode, X.StoreTypeCode, X.StoreCode, X.WarehouseCode,
|
||||||
|
X.ItemTypeCode, X.ItemCode, X.ColorCode, X.ItemDim1Code, X.ItemDim2Code, X.ItemDim3Code
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
I.WarehouseCode AS Depo_Kodu,
|
||||||
|
W.WarehouseDescription AS Depo_Adi,
|
||||||
|
IT.ItemTypeDescription AS InventoryType,
|
||||||
|
I.ItemCode AS Urun_Kodu,
|
||||||
|
AF.ProductDescription AS Madde_Aciklamasi,
|
||||||
|
I.ColorCode AS Renk_Kodu,
|
||||||
|
C.ColorDescription AS Renk_Aciklamasi,
|
||||||
|
I.ItemDim1Code AS Beden,
|
||||||
|
I.ItemDim2Code AS Yaka,
|
||||||
|
ROUND(I.InventoryQty1 - I.PickingQty1 - I.ReserveQty1 - I.DispOrderQty1, U.RoundDigit) AS Kullanilabilir_Envanter,
|
||||||
|
AF.ProductAtt01Desc AS URUN_ANA_GRUBU,
|
||||||
|
AF.ProductAtt02Desc AS URUN_ALT_GRUBU,
|
||||||
|
AF.ProductAtt10Desc AS MARKA,
|
||||||
|
AF.ProductAtt11Desc AS DR,
|
||||||
|
AF.ProductAtt21Desc AS KALIP,
|
||||||
|
AF.ProductAtt22Desc AS IKINCI_PARCA_KALIP,
|
||||||
|
AF.ProductAtt23Desc AS PACA_GENISLIGI,
|
||||||
|
AF.ProductAtt24Desc AS UCUNCU_PARCA_KALIP,
|
||||||
|
AF.ProductAtt25Desc AS UCUNCU_PARCA_MODEL,
|
||||||
|
AF.ProductAtt26Desc AS BIRINCI_PARCA_KUMAS,
|
||||||
|
AF.ProductAtt27Desc AS IKINCI_PARCA_KUMAS,
|
||||||
|
AF.ProductAtt28Desc AS UCUNCU_PARCA_KUMAS,
|
||||||
|
AF.ProductAtt29Desc AS BIRINCI_PARCA_KARISIM,
|
||||||
|
AF.ProductAtt30Desc AS IKINCI_PARCA_KARISIM,
|
||||||
|
AF.ProductAtt31Desc AS UCUNCU_PARCA_KARISIM,
|
||||||
|
AF.ProductAtt32Desc AS YAKA_TIPI,
|
||||||
|
AF.ProductAtt33Desc AS DUGME,
|
||||||
|
AF.ProductAtt34Desc AS YIRTMAC,
|
||||||
|
AF.ProductAtt35Desc AS SEZON_YILI,
|
||||||
|
AF.ProductAtt36Desc AS MEVSIM,
|
||||||
|
AF.ProductAtt37Desc AS TABAN,
|
||||||
|
AF.ProductAtt38Desc AS BIRINCI_PARCA_FIT,
|
||||||
|
AF.ProductAtt39Desc AS IKINCI_PARCA_FIT,
|
||||||
|
AF.ProductAtt40Desc AS BOS2,
|
||||||
|
AF.ProductAtt41Desc AS KISA_KAR,
|
||||||
|
AF.ProductAtt42Desc AS SERI_FASON,
|
||||||
|
AF.ProductAtt43Desc AS STOK_GIRIS_YONTEMI,
|
||||||
|
AF.ProductAtt44Desc AS YETISKIN_GARSON,
|
||||||
|
AF.ProductAtt45Desc AS ASKILI_YAN,
|
||||||
|
AF.ProductAtt46Desc AS BOS3,
|
||||||
|
P.Price AS Fiyat
|
||||||
|
FROM INV I
|
||||||
|
INNER JOIN AttrFiltered AF
|
||||||
|
ON AF.ProductCode = I.ItemCode
|
||||||
|
JOIN cdItem CI WITH (NOLOCK)
|
||||||
|
ON CI.ItemTypeCode = I.ItemTypeCode
|
||||||
|
AND CI.ItemCode = I.ItemCode
|
||||||
|
LEFT JOIN cdUnitOfMeasure U WITH (NOLOCK)
|
||||||
|
ON U.UnitOfMeasureCode = CI.UnitOfMeasureCode1
|
||||||
|
LEFT JOIN cdWarehouseDesc W WITH (NOLOCK)
|
||||||
|
ON W.WarehouseCode = I.WarehouseCode
|
||||||
|
AND W.LangCode = 'TR'
|
||||||
|
LEFT JOIN bsItemTypeDesc IT WITH (NOLOCK)
|
||||||
|
ON IT.ItemTypeCode = I.ItemTypeCode
|
||||||
|
AND IT.LangCode = 'TR'
|
||||||
|
LEFT JOIN cdColorDesc C WITH (NOLOCK)
|
||||||
|
ON C.ColorCode = I.ColorCode
|
||||||
|
AND C.LangCode = 'TR'
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 Price
|
||||||
|
FROM prItemBasePrice PB WITH (NOLOCK)
|
||||||
|
WHERE PB.ItemTypeCode = 1
|
||||||
|
AND PB.ItemCode = I.ItemCode
|
||||||
|
AND LEN(PB.ItemCode) = 13
|
||||||
|
ORDER BY PB.PriceDate DESC
|
||||||
|
) P
|
||||||
|
WHERE
|
||||||
|
I.ItemTypeCode = 1
|
||||||
|
AND LEN(I.ItemCode) = 13
|
||||||
|
AND (I.InventoryQty1 - I.PickingQty1 - I.ReserveQty1 - I.DispOrderQty1) > 0
|
||||||
|
AND CI.IsBlocked = 0
|
||||||
|
AND I.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'
|
||||||
|
);
|
||||||
|
`
|
||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
mssql "github.com/microsoft/go-mssqldb"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
@@ -136,8 +137,7 @@ func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
|
|||||||
|
|
||||||
missing, err := buildMissingVariants(mssql, id, payload.Lines)
|
missing, err := buildMissingVariants(mssql, id, payload.Lines)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ validate error: %v", err)
|
writeDBError(w, http.StatusInternalServerError, "validate_missing_variants", id, "", len(payload.Lines), err)
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,8 +176,7 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
|||||||
|
|
||||||
missing, err := buildMissingVariants(mssql, id, payload.Lines)
|
missing, err := buildMissingVariants(mssql, id, payload.Lines)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ apply validate error: %v", err)
|
writeDBError(w, http.StatusInternalServerError, "apply_validate_missing_variants", id, "", len(payload.Lines), err)
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,7 +201,7 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
|||||||
|
|
||||||
tx, err := mssql.Begin()
|
tx, err := mssql.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
writeDBError(w, http.StatusInternalServerError, "begin_tx", id, username, len(payload.Lines), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
@@ -211,22 +210,19 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
|||||||
if payload.InsertMissing {
|
if payload.InsertMissing {
|
||||||
inserted, err = queries.InsertMissingVariantsTx(tx, missing, username)
|
inserted, err = queries.InsertMissingVariantsTx(tx, missing, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ insert missing error: %v", err)
|
writeDBError(w, http.StatusInternalServerError, "insert_missing_variants", id, username, len(missing), err)
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
|
updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("❌ update order lines error: %v", err)
|
writeDBError(w, http.StatusInternalServerError, "update_order_lines", id, username, len(payload.Lines), err)
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
log.Printf("❌ commit error: %v", err)
|
writeDBError(w, http.StatusInternalServerError, "commit_tx", id, username, len(payload.Lines), err)
|
||||||
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,3 +285,26 @@ func validateUpdateLines(lines []models.OrderProductionUpdateLine) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeDBError(w http.ResponseWriter, status int, step string, orderHeaderID string, username string, lineCount int, err error) {
|
||||||
|
var sqlErr mssql.Error
|
||||||
|
if errors.As(err, &sqlErr) {
|
||||||
|
log.Printf(
|
||||||
|
"❌ SQL error step=%s orderHeaderID=%s user=%s lineCount=%d number=%d state=%d class=%d server=%s proc=%s line=%d message=%s",
|
||||||
|
step, orderHeaderID, username, lineCount,
|
||||||
|
sqlErr.Number, sqlErr.State, sqlErr.Class, sqlErr.ServerName, sqlErr.ProcName, sqlErr.LineNo, sqlErr.Message,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
log.Printf(
|
||||||
|
"❌ DB error step=%s orderHeaderID=%s user=%s lineCount=%d err=%v",
|
||||||
|
step, orderHeaderID, username, lineCount, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.WriteHeader(status)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"message": "Veritabani hatasi",
|
||||||
|
"step": step,
|
||||||
|
"detail": err.Error(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
128
svc/routes/product_stock_query_by_attributes.go
Normal file
128
svc/routes/product_stock_query_by_attributes.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/db"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetProductStockAttributeOptionsHandler
|
||||||
|
// GET /api/product-stock-attribute-options
|
||||||
|
func GetProductStockAttributeOptionsHandler(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rows, err := db.MssqlDB.QueryContext(ctx, queries.GetProductStockAttributeOptionsQuery)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[PRODUCT-STOCK-ATTR-OPTIONS] SQL hatasi: %v", err)
|
||||||
|
http.Error(w, "SQL hatasi: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
result := map[string][]string{
|
||||||
|
"att01": {},
|
||||||
|
"att02": {},
|
||||||
|
"att10": {},
|
||||||
|
"att11": {},
|
||||||
|
"att21": {},
|
||||||
|
"att35": {},
|
||||||
|
"att36": {},
|
||||||
|
"att44": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var fieldName, fieldValue string
|
||||||
|
if err := rows.Scan(&fieldName, &fieldValue); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldName = strings.TrimSpace(fieldName)
|
||||||
|
fieldValue = strings.TrimSpace(fieldValue)
|
||||||
|
if fieldName == "" || fieldValue == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result[fieldName] = append(result[fieldName], fieldValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
http.Error(w, "Satir okuma hatasi: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
_ = json.NewEncoder(w).Encode(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProductStockQueryByAttributesHandler
|
||||||
|
// GET /api/product-stock-query-by-attributes?att01=...&att02=...
|
||||||
|
func GetProductStockQueryByAttributesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
att01 := strings.TrimSpace(r.URL.Query().Get("att01"))
|
||||||
|
att02 := strings.TrimSpace(r.URL.Query().Get("att02"))
|
||||||
|
att10 := strings.TrimSpace(r.URL.Query().Get("att10"))
|
||||||
|
att11 := strings.TrimSpace(r.URL.Query().Get("att11"))
|
||||||
|
att21 := strings.TrimSpace(r.URL.Query().Get("att21"))
|
||||||
|
att35 := strings.TrimSpace(r.URL.Query().Get("att35"))
|
||||||
|
att36 := strings.TrimSpace(r.URL.Query().Get("att36"))
|
||||||
|
att44 := strings.TrimSpace(r.URL.Query().Get("att44"))
|
||||||
|
|
||||||
|
hasAny := att01 != "" || att02 != "" || att10 != "" || att11 != "" || att21 != "" || att35 != "" || att36 != "" || att44 != ""
|
||||||
|
if !hasAny {
|
||||||
|
http.Error(w, "En az bir urun ozelligi secilmelidir", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
start := time.Now()
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
log.Printf(
|
||||||
|
"[PRODUCT-STOCK-BY-ATTRS] request att01=%q att02=%q att10=%q att11=%q att21=%q att35=%q att36=%q att44=%q",
|
||||||
|
att01, att02, att10, att11, att21, att35, att36, att44,
|
||||||
|
)
|
||||||
|
|
||||||
|
rows, err := db.MssqlDB.QueryContext(ctx, queries.GetProductStockQueryByAttributes, att01, att02, att10, att11, att21, att35, att36, att44)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[PRODUCT-STOCK-BY-ATTRS] SQL hatasi: %v", err)
|
||||||
|
http.Error(w, "SQL hatasi: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
columns, err := rows.Columns()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Kolon bilgisi alinamadi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]map[string]interface{}, 0, 256)
|
||||||
|
for rows.Next() {
|
||||||
|
raw := make([]interface{}, len(columns))
|
||||||
|
dest := make([]interface{}, len(columns))
|
||||||
|
for i := range raw {
|
||||||
|
dest[i] = &raw[i]
|
||||||
|
}
|
||||||
|
if err := rows.Scan(dest...); err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
rowMap := make(map[string]interface{}, len(columns))
|
||||||
|
for i, c := range columns {
|
||||||
|
rowMap[c] = normalizeSQLValue(raw[i])
|
||||||
|
}
|
||||||
|
result = append(result, rowMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("[PRODUCT-STOCK-BY-ATTRS] rows err elapsed=%s err=%v", time.Since(start), err)
|
||||||
|
http.Error(w, "Satir okuma hatasi: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("[PRODUCT-STOCK-BY-ATTRS] success rows=%d elapsed=%s", len(result), time.Since(start))
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
_ = json.NewEncoder(w).Encode(result)
|
||||||
|
}
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
||||||
* DO NOT EDIT.
|
|
||||||
*
|
|
||||||
* You are probably looking on adding startup/initialization code.
|
|
||||||
* Use "quasar new boot <name>" and add it there.
|
|
||||||
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
|
||||||
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
||||||
*
|
|
||||||
* Boot files are your "main.js"
|
|
||||||
**/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { Quasar } from 'quasar'
|
|
||||||
import { markRaw } from 'vue'
|
|
||||||
import RootComponent from 'app/src/App.vue'
|
|
||||||
|
|
||||||
import createStore from 'app/src/stores/index'
|
|
||||||
import createRouter from 'app/src/router/index'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default async function (createAppFn, quasarUserOptions) {
|
|
||||||
|
|
||||||
|
|
||||||
// Create the app instance.
|
|
||||||
// Here we inject into it the Quasar UI, the router & possibly the store.
|
|
||||||
const app = createAppFn(RootComponent)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.use(Quasar, quasarUserOptions)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const store = typeof createStore === 'function'
|
|
||||||
? await createStore({})
|
|
||||||
: createStore
|
|
||||||
|
|
||||||
|
|
||||||
app.use(store)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const router = markRaw(
|
|
||||||
typeof createRouter === 'function'
|
|
||||||
? await createRouter({store})
|
|
||||||
: createRouter
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
// make router instance available in store
|
|
||||||
|
|
||||||
store.use(({ store }) => { store.router = router })
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Expose the app, the router and the store.
|
|
||||||
// Note that we are not mounting the app here, since bootstrapping will be
|
|
||||||
// different depending on whether we are in a browser or on the server.
|
|
||||||
return {
|
|
||||||
app,
|
|
||||||
store,
|
|
||||||
router
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
||||||
* DO NOT EDIT.
|
|
||||||
*
|
|
||||||
* You are probably looking on adding startup/initialization code.
|
|
||||||
* Use "quasar new boot <name>" and add it there.
|
|
||||||
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
|
||||||
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
||||||
*
|
|
||||||
* Boot files are your "main.js"
|
|
||||||
**/
|
|
||||||
|
|
||||||
|
|
||||||
import { createApp } from 'vue'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import '@quasar/extras/roboto-font/roboto-font.css'
|
|
||||||
|
|
||||||
import '@quasar/extras/material-icons/material-icons.css'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// We load Quasar stylesheet file
|
|
||||||
import 'quasar/dist/quasar.sass'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import 'src/css/app.css'
|
|
||||||
|
|
||||||
|
|
||||||
import createQuasarApp from './app.js'
|
|
||||||
import quasarUserOptions from './quasar-user-options.js'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const publicPath = `/`
|
|
||||||
|
|
||||||
|
|
||||||
async function start ({
|
|
||||||
app,
|
|
||||||
router
|
|
||||||
, store
|
|
||||||
}, bootFiles) {
|
|
||||||
|
|
||||||
let hasRedirected = false
|
|
||||||
const getRedirectUrl = url => {
|
|
||||||
try { return router.resolve(url).href }
|
|
||||||
catch (err) {}
|
|
||||||
|
|
||||||
return Object(url) === url
|
|
||||||
? null
|
|
||||||
: url
|
|
||||||
}
|
|
||||||
const redirect = url => {
|
|
||||||
hasRedirected = true
|
|
||||||
|
|
||||||
if (typeof url === 'string' && /^https?:\/\//.test(url)) {
|
|
||||||
window.location.href = url
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const href = getRedirectUrl(url)
|
|
||||||
|
|
||||||
// continue if we didn't fail to resolve the url
|
|
||||||
if (href !== null) {
|
|
||||||
window.location.href = href
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlPath = window.location.href.replace(window.location.origin, '')
|
|
||||||
|
|
||||||
for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) {
|
|
||||||
try {
|
|
||||||
await bootFiles[i]({
|
|
||||||
app,
|
|
||||||
router,
|
|
||||||
store,
|
|
||||||
ssrContext: null,
|
|
||||||
redirect,
|
|
||||||
urlPath,
|
|
||||||
publicPath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (err && err.url) {
|
|
||||||
redirect(err.url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
console.error('[Quasar] boot error:', err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasRedirected === true) return
|
|
||||||
|
|
||||||
|
|
||||||
app.use(router)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.mount('#q-app')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
createQuasarApp(createApp, quasarUserOptions)
|
|
||||||
|
|
||||||
.then(app => {
|
|
||||||
// eventually remove this when Cordova/Capacitor/Electron support becomes old
|
|
||||||
const [ method, mapFn ] = Promise.allSettled !== void 0
|
|
||||||
? [
|
|
||||||
'allSettled',
|
|
||||||
bootFiles => bootFiles.map(result => {
|
|
||||||
if (result.status === 'rejected') {
|
|
||||||
console.error('[Quasar] boot error:', result.reason)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return result.value.default
|
|
||||||
})
|
|
||||||
]
|
|
||||||
: [
|
|
||||||
'all',
|
|
||||||
bootFiles => bootFiles.map(entry => entry.default)
|
|
||||||
]
|
|
||||||
|
|
||||||
return Promise[ method ]([
|
|
||||||
|
|
||||||
import(/* webpackMode: "eager" */ 'boot/dayjs')
|
|
||||||
|
|
||||||
]).then(bootFiles => {
|
|
||||||
const boot = mapFn(bootFiles).filter(entry => typeof entry === 'function')
|
|
||||||
start(app, boot)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
||||||
* DO NOT EDIT.
|
|
||||||
*
|
|
||||||
* You are probably looking on adding startup/initialization code.
|
|
||||||
* Use "quasar new boot <name>" and add it there.
|
|
||||||
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
|
||||||
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
||||||
*
|
|
||||||
* Boot files are your "main.js"
|
|
||||||
**/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import App from 'app/src/App.vue'
|
|
||||||
let appPrefetch = typeof App.preFetch === 'function'
|
|
||||||
? App.preFetch
|
|
||||||
: (
|
|
||||||
// Class components return the component options (and the preFetch hook) inside __c property
|
|
||||||
App.__c !== void 0 && typeof App.__c.preFetch === 'function'
|
|
||||||
? App.__c.preFetch
|
|
||||||
: false
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
function getMatchedComponents (to, router) {
|
|
||||||
const route = to
|
|
||||||
? (to.matched ? to : router.resolve(to).route)
|
|
||||||
: router.currentRoute.value
|
|
||||||
|
|
||||||
if (!route) { return [] }
|
|
||||||
|
|
||||||
const matched = route.matched.filter(m => m.components !== void 0)
|
|
||||||
|
|
||||||
if (matched.length === 0) { return [] }
|
|
||||||
|
|
||||||
return Array.prototype.concat.apply([], matched.map(m => {
|
|
||||||
return Object.keys(m.components).map(key => {
|
|
||||||
const comp = m.components[key]
|
|
||||||
return {
|
|
||||||
path: m.path,
|
|
||||||
c: comp
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addPreFetchHooks ({ router, store, publicPath }) {
|
|
||||||
// Add router hook for handling preFetch.
|
|
||||||
// Doing it after initial route is resolved so that we don't double-fetch
|
|
||||||
// the data that we already have. Using router.beforeResolve() so that all
|
|
||||||
// async components are resolved.
|
|
||||||
router.beforeResolve((to, from, next) => {
|
|
||||||
const
|
|
||||||
urlPath = window.location.href.replace(window.location.origin, ''),
|
|
||||||
matched = getMatchedComponents(to, router),
|
|
||||||
prevMatched = getMatchedComponents(from, router)
|
|
||||||
|
|
||||||
let diffed = false
|
|
||||||
const preFetchList = matched
|
|
||||||
.filter((m, i) => {
|
|
||||||
return diffed || (diffed = (
|
|
||||||
!prevMatched[i] ||
|
|
||||||
prevMatched[i].c !== m.c ||
|
|
||||||
m.path.indexOf('/:') > -1 // does it has params?
|
|
||||||
))
|
|
||||||
})
|
|
||||||
.filter(m => m.c !== void 0 && (
|
|
||||||
typeof m.c.preFetch === 'function'
|
|
||||||
// Class components return the component options (and the preFetch hook) inside __c property
|
|
||||||
|| (m.c.__c !== void 0 && typeof m.c.__c.preFetch === 'function')
|
|
||||||
))
|
|
||||||
.map(m => m.c.__c !== void 0 ? m.c.__c.preFetch : m.c.preFetch)
|
|
||||||
|
|
||||||
|
|
||||||
if (appPrefetch !== false) {
|
|
||||||
preFetchList.unshift(appPrefetch)
|
|
||||||
appPrefetch = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (preFetchList.length === 0) {
|
|
||||||
return next()
|
|
||||||
}
|
|
||||||
|
|
||||||
let hasRedirected = false
|
|
||||||
const redirect = url => {
|
|
||||||
hasRedirected = true
|
|
||||||
next(url)
|
|
||||||
}
|
|
||||||
const proceed = () => {
|
|
||||||
|
|
||||||
if (hasRedirected === false) { next() }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
preFetchList.reduce(
|
|
||||||
(promise, preFetch) => promise.then(() => hasRedirected === false && preFetch({
|
|
||||||
store,
|
|
||||||
currentRoute: to,
|
|
||||||
previousRoute: from,
|
|
||||||
redirect,
|
|
||||||
urlPath,
|
|
||||||
publicPath
|
|
||||||
})),
|
|
||||||
Promise.resolve()
|
|
||||||
)
|
|
||||||
.then(proceed)
|
|
||||||
.catch(e => {
|
|
||||||
console.error(e)
|
|
||||||
proceed()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
|
||||||
* DO NOT EDIT.
|
|
||||||
*
|
|
||||||
* You are probably looking on adding startup/initialization code.
|
|
||||||
* Use "quasar new boot <name>" and add it there.
|
|
||||||
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
|
||||||
* boot: ['file', ...] // do not add ".js" extension to it.
|
|
||||||
*
|
|
||||||
* Boot files are your "main.js"
|
|
||||||
**/
|
|
||||||
|
|
||||||
import lang from 'quasar/lang/tr.js'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import {Loading,Dialog,Notify} from 'quasar'
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default { config: {"notify":{"position":"top","timeout":2500}},lang,plugins: {Loading,Dialog,Notify} }
|
|
||||||
|
|
||||||
@@ -270,6 +270,11 @@ const menuItems = [
|
|||||||
label: 'Ürün Kodundan Stok Sorgula',
|
label: 'Ürün Kodundan Stok Sorgula',
|
||||||
to: '/app/product-stock-query',
|
to: '/app/product-stock-query',
|
||||||
permission: 'order:view'
|
permission: 'order:view'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Ürün Özelliklerinden Stok Bul',
|
||||||
|
to: '/app/product-stock-by-attributes',
|
||||||
|
permission: 'order:view'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -556,7 +556,14 @@ async function onBulkSubmit () {
|
|||||||
await store.fetchItems(orderHeaderID.value)
|
await store.fetchItems(orderHeaderID.value)
|
||||||
selectedMap.value = {}
|
selectedMap.value = {}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$q.notify({ type: 'negative', message: 'Toplu kayit islemi basarisiz.' })
|
console.error('[OrderProductionUpdate] onBulkSubmit failed', {
|
||||||
|
orderHeaderID: orderHeaderID.value,
|
||||||
|
selectedRowCount: selectedRows.length,
|
||||||
|
lineCount: lines.length,
|
||||||
|
apiError: err?.response?.data,
|
||||||
|
message: err?.message
|
||||||
|
})
|
||||||
|
$q.notify({ type: 'negative', message: store.error || 'Toplu kayit islemi basarisiz.' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
1127
ui/src/pages/ProductStockByAttributes.vue
Normal file
1127
ui/src/pages/ProductStockByAttributes.vue
Normal file
@@ -0,0 +1,1127 @@
|
|||||||
|
<template>
|
||||||
|
<q-page
|
||||||
|
v-if="canReadOrder"
|
||||||
|
class="order-page q-pa-md"
|
||||||
|
:style="{ '--grid-header-h': gridHeaderHeight }"
|
||||||
|
>
|
||||||
|
<div class="sticky-stack">
|
||||||
|
<div class="filter-bar row q-col-gutter-md q-mb-sm">
|
||||||
|
<div
|
||||||
|
v-for="def in attrDefs"
|
||||||
|
:key="def.key"
|
||||||
|
class="col-12 col-md-3"
|
||||||
|
>
|
||||||
|
<q-select
|
||||||
|
v-model="filters[def.key]"
|
||||||
|
:options="filteredAttrOptions[def.key] || []"
|
||||||
|
:label="def.label"
|
||||||
|
filled
|
||||||
|
dense
|
||||||
|
clearable
|
||||||
|
use-input
|
||||||
|
input-debounce="250"
|
||||||
|
:loading="loadingAttributeOptions"
|
||||||
|
@filter="(val, update) => filterAttributeOptions(def.key, val, update)"
|
||||||
|
@keyup.enter="fetchStockByAttributes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
icon="search"
|
||||||
|
label="Sorgula"
|
||||||
|
:loading="loadingStock"
|
||||||
|
:disable="!hasAnyFilter"
|
||||||
|
@click="fetchStockByAttributes"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="grey-8"
|
||||||
|
icon="restart_alt"
|
||||||
|
label="Sıfırla"
|
||||||
|
@click="resetForm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="primary"
|
||||||
|
icon="unfold_more"
|
||||||
|
:label="allDetailsExpanded ? 'Tüm Depoları Kapat' : 'Tüm Depoları Göster'"
|
||||||
|
:disable="!level1Groups.length"
|
||||||
|
@click="toggleAllDetails"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="save-toolbar">
|
||||||
|
<div class="text-subtitle2 text-weight-bold">Ürün Özelliklerinden Stok Bul</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="showGridHeader"
|
||||||
|
class="order-grid-header"
|
||||||
|
>
|
||||||
|
<div class="col-fixed model">MODEL</div>
|
||||||
|
<div class="col-fixed renk">RENK</div>
|
||||||
|
<div class="col-fixed ana">ÜRÜN ANA GRUBU</div>
|
||||||
|
<div class="col-fixed alt">ÜRÜN ALT GRUBU</div>
|
||||||
|
<div class="col-fixed aciklama-col">AÇIKLAMA</div>
|
||||||
|
|
||||||
|
<div class="beden-block">
|
||||||
|
<div class="grp-row">
|
||||||
|
<div class="grp-title">{{ activeSchema?.title || 'BEDEN' }}</div>
|
||||||
|
<div class="grp-body">
|
||||||
|
<div
|
||||||
|
v-for="v in sizeLabels"
|
||||||
|
:key="'hdr-' + activeGrpKey + '-' + v"
|
||||||
|
class="grp-cell hdr"
|
||||||
|
>
|
||||||
|
{{ v }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="total-row">
|
||||||
|
<div class="total-cell">ADET</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-banner
|
||||||
|
v-if="errorMessage"
|
||||||
|
class="bg-red-1 text-negative q-my-sm rounded-borders"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
{{ errorMessage }}
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<q-banner
|
||||||
|
v-else-if="!loadingStock && !level1Groups.length"
|
||||||
|
class="bg-blue-1 text-primary q-my-sm rounded-borders"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
Ürün özelliği seçip sorgulayın.
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<div class="order-scroll-y">
|
||||||
|
<div v-if="level1Groups.length" class="order-grid-body">
|
||||||
|
<template v-for="grp1 in level1Groups" :key="grp1.key">
|
||||||
|
<div class="summary-group open">
|
||||||
|
<div class="order-sub-header level-1" @click="toggleOpen(grp1.key)">
|
||||||
|
<div class="sub-col level1-merged">
|
||||||
|
<div class="text-weight-bold">{{ grp1.productCode }}</div>
|
||||||
|
<div class="text-caption">{{ grp1.productDesc || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sub-center level1-center">
|
||||||
|
<div class="beden-row values-top">
|
||||||
|
<div v-for="sz in sizeLabels" :key="`v1-${grp1.key}-${sz}`" class="beden-cell">
|
||||||
|
{{ Number(grp1.sizeTotals[sz] || 0) > 0 ? formatNumber(grp1.sizeTotals[sz]) : '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="beden-row headers">
|
||||||
|
<div v-for="sz in sizeLabels" :key="`h1-${grp1.key}-${sz}`" class="beden-cell">
|
||||||
|
{{ sz }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sub-right level1-right">
|
||||||
|
<div class="top-total">{{ formatNumber(grp1.totalQty) }}</div>
|
||||||
|
<div class="bottom-row">
|
||||||
|
<div class="bottom-label">ADET</div>
|
||||||
|
</div>
|
||||||
|
<div class="icon-row">
|
||||||
|
<q-icon :name="isOpen(grp1.key) ? 'expand_less' : 'expand_more'" size="18px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="isOpen(grp1.key)">
|
||||||
|
<template v-for="grp2 in grp1.children" :key="grp2.key">
|
||||||
|
<div class="order-sub-header level-2" @click="onLevel2Click(grp1.productCode, grp2)">
|
||||||
|
<div class="sub-col model">{{ grp1.productCode || '-' }}</div>
|
||||||
|
<div class="sub-col renk">
|
||||||
|
<div class="renk-kodu">{{ grp2.colorCode || '-' }}{{ grp2.secondColor ? '-' + grp2.secondColor : '' }}</div>
|
||||||
|
<div class="renk-aciklama">{{ grp2.colorDesc || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="sub-col ana">{{ grp2.urunAnaGrubu || '-' }}</div>
|
||||||
|
<div class="sub-col alt">{{ grp2.urunAltGrubu || '-' }}</div>
|
||||||
|
<div class="sub-col aciklama">{{ grp2.aciklama || '-' }}</div>
|
||||||
|
|
||||||
|
<div class="sub-center level2-center">
|
||||||
|
<div class="beden-row values-top">
|
||||||
|
<div v-for="sz in sizeLabels" :key="`top2-${grp2.key}-${sz}`" class="beden-cell">
|
||||||
|
{{ Number(grp2.sizeTotals[sz] || 0) > 0 ? formatNumber(grp2.sizeTotals[sz]) : '' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="beden-row headers">
|
||||||
|
<div v-for="sz in sizeLabels" :key="`h2-${grp2.key}-${sz}`" class="beden-cell">
|
||||||
|
{{ sz }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="sub-right level2-right">
|
||||||
|
<div class="top-total">{{ formatNumber(grp2.totalQty) }}</div>
|
||||||
|
<div class="bottom-row">
|
||||||
|
<div class="bottom-label">ADET</div>
|
||||||
|
<q-icon :name="isOpen(grp2.key) ? 'expand_less' : 'expand_more'" size="18px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="isOpen(grp2.key)">
|
||||||
|
<div class="detail-table-wrap">
|
||||||
|
<div
|
||||||
|
v-for="row in buildLevel2Rows(grp2)"
|
||||||
|
:key="row.rowKey"
|
||||||
|
class="summary-row"
|
||||||
|
>
|
||||||
|
<div class="cell depo-merged">{{ row.depoAdi || '-' }}</div>
|
||||||
|
|
||||||
|
<div class="grp-area">
|
||||||
|
<div class="grp-row">
|
||||||
|
<div
|
||||||
|
v-for="v in sizeLabels"
|
||||||
|
:key="row.rowKey + '-sz-' + v"
|
||||||
|
class="cell beden"
|
||||||
|
>
|
||||||
|
{{ resolveBedenValue(row.bedenMap, row.grpKey, v) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cell adet">{{ formatNumber(row.adet) }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">Bu modüle erişim yetkiniz yok.</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import api from 'src/services/api'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import {
|
||||||
|
detectBedenGroup,
|
||||||
|
normalizeBedenLabel,
|
||||||
|
schemaByKey as storeSchemaByKey,
|
||||||
|
useOrderEntryStore
|
||||||
|
} from 'src/stores/orderentryStore'
|
||||||
|
|
||||||
|
const $q = useQuasar()
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
const orderStore = useOrderEntryStore()
|
||||||
|
|
||||||
|
const attrDefs = [
|
||||||
|
{ key: 'att01', label: 'Urun Ana Grubu' },
|
||||||
|
{ key: 'att02', label: 'Urun Alt Grubu' },
|
||||||
|
{ key: 'att10', label: 'Marka' },
|
||||||
|
{ key: 'att11', label: 'DR' },
|
||||||
|
{ key: 'att21', label: 'Kalip' },
|
||||||
|
{ key: 'att35', label: 'Sezon Yili' },
|
||||||
|
{ key: 'att36', label: 'Mevsim' },
|
||||||
|
{ key: 'att44', label: 'Yetiskin/Garson' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const loadingAttributeOptions = ref(false)
|
||||||
|
const loadingStock = ref(false)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
const filters = ref({
|
||||||
|
att01: '',
|
||||||
|
att02: '',
|
||||||
|
att10: '',
|
||||||
|
att11: '',
|
||||||
|
att21: '',
|
||||||
|
att35: '',
|
||||||
|
att36: '',
|
||||||
|
att44: ''
|
||||||
|
})
|
||||||
|
const attributeOptions = ref({})
|
||||||
|
const filteredAttrOptions = ref({})
|
||||||
|
const rawRows = ref([])
|
||||||
|
const activeSchema = ref(storeSchemaByKey.tak)
|
||||||
|
const activeGrpKey = ref('tak')
|
||||||
|
const openState = ref({})
|
||||||
|
const hasAnyFilter = computed(() =>
|
||||||
|
attrDefs.some((def) => String(filters.value?.[def.key] || '').trim() !== '')
|
||||||
|
)
|
||||||
|
|
||||||
|
const sizeLabels = computed(() => activeSchema.value?.values || [])
|
||||||
|
const showGridHeader = computed(() =>
|
||||||
|
!loadingStock.value && level1Groups.value.length > 0
|
||||||
|
)
|
||||||
|
const allDetailsExpanded = computed(() => {
|
||||||
|
const groups = level1Groups.value || []
|
||||||
|
if (!groups.length) return false
|
||||||
|
const detailKeys = []
|
||||||
|
for (const g1 of groups) {
|
||||||
|
for (const g2 of g1.children || []) {
|
||||||
|
detailKeys.push(g2.key)
|
||||||
|
for (const g3 of g2.children || []) detailKeys.push(g3.key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!detailKeys.length) return false
|
||||||
|
return detailKeys.every((k) => openState.value[k] === true)
|
||||||
|
})
|
||||||
|
const gridHeaderHeight = computed(() =>
|
||||||
|
showGridHeader.value ? '56px' : '0px'
|
||||||
|
)
|
||||||
|
|
||||||
|
function emptySizeTotals() {
|
||||||
|
const map = {}
|
||||||
|
for (const s of sizeLabels.value) map[s] = 0
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseNumber(value) {
|
||||||
|
if (typeof value === 'number') return Number.isFinite(value) ? value : 0
|
||||||
|
const text = String(value ?? '').trim()
|
||||||
|
if (!text) return 0
|
||||||
|
const normalized = text.replace(/\./g, '').replace(',', '.')
|
||||||
|
const n = Number.parseFloat(normalized)
|
||||||
|
return Number.isFinite(n) ? n : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatNumber(v) {
|
||||||
|
return parseNumber(v).toLocaleString('tr-TR', { minimumFractionDigits: 0, maximumFractionDigits: 2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSize(v) {
|
||||||
|
return normalizeBedenLabel(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveBedenValue(bedenMap, grpKey, bedenLabel) {
|
||||||
|
const map = bedenMap?.[grpKey]
|
||||||
|
if (!map || typeof map !== 'object') return 0
|
||||||
|
return Number(map[bedenLabel] || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOpen(key) {
|
||||||
|
return openState.value[key] !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleOpen(key) {
|
||||||
|
openState.value[key] = !isOpen(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function initOpenState(groups, expandDetails = false) {
|
||||||
|
const next = {}
|
||||||
|
for (const g1 of groups) {
|
||||||
|
next[g1.key] = true
|
||||||
|
for (const g2 of g1.children) {
|
||||||
|
next[g2.key] = expandDetails
|
||||||
|
for (const g3 of g2.children) {
|
||||||
|
next[g3.key] = expandDetails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
openState.value = next
|
||||||
|
}
|
||||||
|
|
||||||
|
function expandAllDetails() {
|
||||||
|
initOpenState(level1Groups.value || [], true)
|
||||||
|
}
|
||||||
|
|
||||||
|
function collapseAllDetails() {
|
||||||
|
initOpenState(level1Groups.value || [], false)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAllDetails() {
|
||||||
|
if (allDetailsExpanded.value) {
|
||||||
|
collapseAllDetails()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
expandAllDetails()
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLevel3Rows(grp3) {
|
||||||
|
const byKey = new Map()
|
||||||
|
const gk = activeGrpKey.value || 'tak'
|
||||||
|
|
||||||
|
for (const item of grp3.items || []) {
|
||||||
|
const model = String(item.Urun_Kodu || '').trim()
|
||||||
|
const renk = String(item.Renk_Kodu || '').trim()
|
||||||
|
const renk2 = String(item.Yaka || '').trim()
|
||||||
|
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
||||||
|
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
||||||
|
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
||||||
|
const beden = normalizeSize(item.Beden || '')
|
||||||
|
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
||||||
|
|
||||||
|
const rowKey = [model, renk, renk2, grp3.depoKodu || '', grp3.depoAdi || ''].join('|')
|
||||||
|
if (!byKey.has(rowKey)) {
|
||||||
|
byKey.set(rowKey, {
|
||||||
|
rowKey,
|
||||||
|
model,
|
||||||
|
renk,
|
||||||
|
renk2,
|
||||||
|
urunAnaGrubu,
|
||||||
|
urunAltGrubu,
|
||||||
|
aciklama,
|
||||||
|
grpKey: gk,
|
||||||
|
bedenMap: { [gk]: {} },
|
||||||
|
adet: 0,
|
||||||
|
depoAdi: grp3.depoAdi || '-'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const row = byKey.get(rowKey)
|
||||||
|
row.bedenMap[gk][beden] = Number(row.bedenMap[gk][beden] || 0) + qty
|
||||||
|
row.adet += qty
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(byKey.values())
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLevel2Rows(grp2) {
|
||||||
|
const merged = []
|
||||||
|
for (const grp3 of grp2.children || []) {
|
||||||
|
merged.push(...buildLevel3Rows(grp3))
|
||||||
|
}
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
const level1Groups = computed(() => {
|
||||||
|
const l1Map = new Map()
|
||||||
|
|
||||||
|
for (const item of rawRows.value) {
|
||||||
|
const productCode = String(item.Urun_Kodu || '').trim()
|
||||||
|
const productDesc = String(item.Madde_Aciklamasi || '').trim()
|
||||||
|
const colorCode = String(item.Renk_Kodu || '').trim()
|
||||||
|
const colorDesc = String(item.Renk_Aciklamasi || '').trim()
|
||||||
|
const secondColor = String(item.Yaka || '').trim()
|
||||||
|
const depoKodu = String(item.Depo_Kodu || '').trim()
|
||||||
|
const depoAdi = String(item.Depo_Adi || '').trim()
|
||||||
|
const urunAnaGrubu = String(item.URUN_ANA_GRUBU || '').trim()
|
||||||
|
const urunAltGrubu = String(item.URUN_ALT_GRUBU || '').trim()
|
||||||
|
const aciklama = String(item.Madde_Aciklamasi || '').trim()
|
||||||
|
const beden = normalizeSize(item.Beden || '')
|
||||||
|
const qty = parseNumber(item.Kullanilabilir_Envanter)
|
||||||
|
|
||||||
|
if (!l1Map.has(productCode)) {
|
||||||
|
l1Map.set(productCode, {
|
||||||
|
key: `L1|${productCode}`,
|
||||||
|
productCode,
|
||||||
|
productDesc,
|
||||||
|
sizeTotals: emptySizeTotals(),
|
||||||
|
totalQty: 0,
|
||||||
|
childrenMap: new Map()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const l1 = l1Map.get(productCode)
|
||||||
|
if (Object.prototype.hasOwnProperty.call(l1.sizeTotals, beden)) {
|
||||||
|
l1.sizeTotals[beden] += qty
|
||||||
|
}
|
||||||
|
l1.totalQty += qty
|
||||||
|
|
||||||
|
const l2Key = `${colorCode}|${secondColor}`
|
||||||
|
if (!l1.childrenMap.has(l2Key)) {
|
||||||
|
l1.childrenMap.set(l2Key, {
|
||||||
|
key: `L2|${productCode}|${l2Key}`,
|
||||||
|
colorCode,
|
||||||
|
colorDesc,
|
||||||
|
secondColor,
|
||||||
|
urunAnaGrubu,
|
||||||
|
urunAltGrubu,
|
||||||
|
aciklama,
|
||||||
|
sizeTotals: emptySizeTotals(),
|
||||||
|
totalQty: 0,
|
||||||
|
childrenMap: new Map()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const l2 = l1.childrenMap.get(l2Key)
|
||||||
|
if (Object.prototype.hasOwnProperty.call(l2.sizeTotals, beden)) {
|
||||||
|
l2.sizeTotals[beden] += qty
|
||||||
|
}
|
||||||
|
l2.totalQty += qty
|
||||||
|
|
||||||
|
const l3Key = `${depoKodu}|${depoAdi}`
|
||||||
|
if (!l2.childrenMap.has(l3Key)) {
|
||||||
|
l2.childrenMap.set(l3Key, {
|
||||||
|
key: `L3|${productCode}|${l2Key}|${l3Key}`,
|
||||||
|
depoKodu,
|
||||||
|
depoAdi,
|
||||||
|
sizeTotals: emptySizeTotals(),
|
||||||
|
totalQty: 0,
|
||||||
|
items: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const l3 = l2.childrenMap.get(l3Key)
|
||||||
|
if (Object.prototype.hasOwnProperty.call(l3.sizeTotals, beden)) {
|
||||||
|
l3.sizeTotals[beden] += qty
|
||||||
|
}
|
||||||
|
l3.totalQty += qty
|
||||||
|
l3.items.push({
|
||||||
|
...item,
|
||||||
|
_rowKey: `${productCode}|${colorCode}|${secondColor}|${depoKodu}|${beden}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.from(l1Map.values()).map((l1) => ({
|
||||||
|
...l1,
|
||||||
|
children: Array.from(l1.childrenMap.values()).map((l2) => ({
|
||||||
|
...l2,
|
||||||
|
children: Array.from(l2.childrenMap.values())
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
function filterAttributeOptions(field, val, update) {
|
||||||
|
const source = Array.isArray(attributeOptions.value?.[field])
|
||||||
|
? attributeOptions.value[field]
|
||||||
|
: []
|
||||||
|
if (!val) {
|
||||||
|
update(() => {
|
||||||
|
filteredAttrOptions.value[field] = [...source]
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const needle = String(val || '').toLocaleLowerCase('tr-TR')
|
||||||
|
update(() => {
|
||||||
|
filteredAttrOptions.value[field] = source.filter((opt) =>
|
||||||
|
String(opt || '').toLocaleLowerCase('tr-TR').includes(needle)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadAttributeOptions() {
|
||||||
|
loadingAttributeOptions.value = true
|
||||||
|
try {
|
||||||
|
const res = await api.get('/product-stock-attribute-options')
|
||||||
|
const payload = res?.data && typeof res.data === 'object' ? res.data : {}
|
||||||
|
const next = {}
|
||||||
|
const nextFiltered = {}
|
||||||
|
|
||||||
|
for (const def of attrDefs) {
|
||||||
|
const arr = Array.isArray(payload?.[def.key]) ? payload[def.key] : []
|
||||||
|
const list = arr
|
||||||
|
.map((x) => String(x || '').trim())
|
||||||
|
.filter((x) => x.length > 0)
|
||||||
|
.sort((a, b) => a.localeCompare(b, 'tr'))
|
||||||
|
next[def.key] = list
|
||||||
|
nextFiltered[def.key] = [...list]
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeOptions.value = next
|
||||||
|
filteredAttrOptions.value = nextFiltered
|
||||||
|
} catch (err) {
|
||||||
|
errorMessage.value = 'Urun ozellik secenekleri alinamadi.'
|
||||||
|
console.error('loadAttributeOptions error:', err)
|
||||||
|
} finally {
|
||||||
|
loadingAttributeOptions.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchStockByAttributes() {
|
||||||
|
if (!hasAnyFilter.value) return
|
||||||
|
|
||||||
|
const params = {}
|
||||||
|
for (const def of attrDefs) {
|
||||||
|
const val = String(filters.value?.[def.key] || '').trim()
|
||||||
|
if (val) params[def.key] = val
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingStock.value = true
|
||||||
|
errorMessage.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!orderStore.schemaMap || !Object.keys(orderStore.schemaMap).length) {
|
||||||
|
orderStore.initSchemaMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await api.get('/product-stock-query-by-attributes', { params })
|
||||||
|
const list = Array.isArray(res?.data) ? res.data : []
|
||||||
|
|
||||||
|
if (!list.length) {
|
||||||
|
rawRows.value = []
|
||||||
|
openState.value = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const first = list[0] || {}
|
||||||
|
const grpKey = detectBedenGroup(
|
||||||
|
list.map((x) => x?.Beden || ''),
|
||||||
|
first?.URUN_ANA_GRUBU || '',
|
||||||
|
first?.YETISKIN_GARSON || ''
|
||||||
|
)
|
||||||
|
|
||||||
|
const schemaMap = Object.keys(orderStore.schemaMap || {}).length
|
||||||
|
? orderStore.schemaMap
|
||||||
|
: storeSchemaByKey
|
||||||
|
activeGrpKey.value = grpKey || 'tak'
|
||||||
|
activeSchema.value = schemaMap?.[grpKey] || storeSchemaByKey.tak
|
||||||
|
|
||||||
|
rawRows.value = list
|
||||||
|
initOpenState(level1Groups.value)
|
||||||
|
} catch (err) {
|
||||||
|
console.error('fetchStockByAttributes error:', err)
|
||||||
|
rawRows.value = []
|
||||||
|
openState.value = {}
|
||||||
|
errorMessage.value = 'Stok sorgulama sirasinda hata olustu.'
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
position: 'top-right',
|
||||||
|
message: 'Stok sorgusu basarisiz.'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
loadingStock.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLevel2Click(_productCode, grp2) {
|
||||||
|
toggleOpen(grp2.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetForm() {
|
||||||
|
filters.value = {
|
||||||
|
att01: '',
|
||||||
|
att02: '',
|
||||||
|
att10: '',
|
||||||
|
att11: '',
|
||||||
|
att21: '',
|
||||||
|
att35: '',
|
||||||
|
att36: '',
|
||||||
|
att44: ''
|
||||||
|
}
|
||||||
|
rawRows.value = []
|
||||||
|
errorMessage.value = ''
|
||||||
|
openState.value = {}
|
||||||
|
activeSchema.value = storeSchemaByKey.tak
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadAttributeOptions()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.order-page {
|
||||||
|
--psq-sticky-offset: 12px;
|
||||||
|
--grp-title-w: 44px;
|
||||||
|
--psq-header-h: 56px;
|
||||||
|
--psq-col-adet: calc(var(--col-adet) + var(--beden-w));
|
||||||
|
--psq-l1-lift: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header {
|
||||||
|
top: calc(var(--header-h) + var(--filter-h) + var(--save-h) + var(--psq-sticky-offset)) !important;
|
||||||
|
grid-template-columns:
|
||||||
|
var(--col-model)
|
||||||
|
var(--col-renk)
|
||||||
|
var(--col-ana)
|
||||||
|
var(--col-alt)
|
||||||
|
var(--col-aciklama)
|
||||||
|
calc(var(--grp-title-w) + var(--grp-title-gap) + (var(--beden-w)*var(--beden-count)))
|
||||||
|
var(--psq-col-adet) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .col-fixed,
|
||||||
|
.order-grid-header .total-cell {
|
||||||
|
writing-mode: horizontal-tb !important;
|
||||||
|
transform: none !important;
|
||||||
|
height: var(--psq-header-h) !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding: 0 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .beden-block {
|
||||||
|
height: var(--psq-header-h) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .grp-row {
|
||||||
|
height: var(--psq-header-h) !important;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .grp-title {
|
||||||
|
width: var(--grp-title-w) !important;
|
||||||
|
text-align: center !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .grp-cell.hdr {
|
||||||
|
height: var(--psq-header-h) !important;
|
||||||
|
font-size: 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .total-row {
|
||||||
|
grid-column: 7 / -1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .total-cell {
|
||||||
|
width: var(--psq-col-adet) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-header .total-cell:last-child {
|
||||||
|
width: var(--psq-col-adet) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header {
|
||||||
|
grid-template-columns:
|
||||||
|
var(--col-model)
|
||||||
|
var(--col-renk)
|
||||||
|
var(--col-ana)
|
||||||
|
var(--col-alt)
|
||||||
|
var(--col-aciklama)
|
||||||
|
calc(var(--grp-title-w) + var(--grp-title-gap) + (var(--beden-w)*var(--beden-count)))
|
||||||
|
var(--psq-col-adet) !important;
|
||||||
|
top: calc(
|
||||||
|
var(--header-h)
|
||||||
|
+ var(--filter-h)
|
||||||
|
+ var(--save-h)
|
||||||
|
+ var(--grid-header-h)
|
||||||
|
+ var(--psq-sticky-offset)
|
||||||
|
) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 {
|
||||||
|
min-height: 82px !important;
|
||||||
|
height: 82px !important;
|
||||||
|
background: #fff9c4 !important;
|
||||||
|
border-top: 1px solid #d4c79f !important;
|
||||||
|
border-bottom: 1px solid #d4c79f !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 {
|
||||||
|
min-height: 84px !important;
|
||||||
|
height: 84px !important;
|
||||||
|
top: calc(
|
||||||
|
var(--header-h)
|
||||||
|
+ var(--filter-h)
|
||||||
|
+ var(--save-h)
|
||||||
|
+ var(--grid-header-h)
|
||||||
|
+ var(--psq-sticky-offset)
|
||||||
|
- var(--psq-l1-lift)
|
||||||
|
) !important;
|
||||||
|
background: var(--q-primary, #1976d2) !important;
|
||||||
|
border-top: 1px solid #145ea8 !important;
|
||||||
|
border-bottom: 1px solid #145ea8 !important;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-col.level1-merged {
|
||||||
|
grid-column: 1 / 6;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-col.level1-merged .text-caption {
|
||||||
|
color: #fff !important;
|
||||||
|
opacity: 0.95;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-center.level1-center {
|
||||||
|
grid-column: 6;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 42px 42px;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding-left: calc(var(--grp-title-w) + var(--grp-title-gap));
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .beden-row {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: var(--beden-w);
|
||||||
|
height: 42px;
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .beden-row .beden-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 42px;
|
||||||
|
min-height: 42px;
|
||||||
|
background: var(--q-primary, #1976d2) !important;
|
||||||
|
color: #fff !important;
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
border-top: none;
|
||||||
|
border-left: none;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .beden-row.values-top .beden-cell {
|
||||||
|
background: #fff9c4 !important;
|
||||||
|
color: var(--q-primary, #1976d2) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .beden-row.headers .beden-cell {
|
||||||
|
font-weight: 500;
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-right.level1-right {
|
||||||
|
grid-column: 7 / 8;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--psq-col-adet);
|
||||||
|
grid-template-rows: 30px 30px 24px;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-items: stretch;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0 8px 0 6px;
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-right .top-total {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: #fff9c4 !important;
|
||||||
|
color: var(--q-primary, #1976d2) !important;
|
||||||
|
padding: 0 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-right .bottom-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-right .bottom-row {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 4px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-1 .sub-right .icon-row {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 3;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #111;
|
||||||
|
min-width: 0;
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
white-space: normal;
|
||||||
|
overflow: visible;
|
||||||
|
text-overflow: clip;
|
||||||
|
line-height: 1.2;
|
||||||
|
word-break: break-word;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.model { grid-column: 1; }
|
||||||
|
.order-sub-header.level-2 .sub-col.renk { grid-column: 2; }
|
||||||
|
.order-sub-header.level-2 .sub-col.ana { grid-column: 3; }
|
||||||
|
.order-sub-header.level-2 .sub-col.alt { grid-column: 4; }
|
||||||
|
.order-sub-header.level-2 .sub-col.aciklama { grid-column: 5; }
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.model,
|
||||||
|
.order-sub-header.level-2 .sub-col.renk,
|
||||||
|
.order-sub-header.level-2 .sub-col.ana,
|
||||||
|
.order-sub-header.level-2 .sub-col.alt {
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.renk {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2px;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.renk .renk-kodu {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.renk .renk-aciklama {
|
||||||
|
font-size: 11px;
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-col.aciklama {
|
||||||
|
justify-content: flex-start;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-center.level2-center {
|
||||||
|
grid-column: 6;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
align-items: stretch;
|
||||||
|
height: 100%;
|
||||||
|
padding-left: var(--grp-title-w);
|
||||||
|
margin-left: var(--grp-title-gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .beden-row {
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: var(--beden-w);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .beden-row.values-top .beden-cell {
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
background: transparent;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f1f1f;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .beden-row.headers .beden-cell {
|
||||||
|
border-top: 1px solid #d4c79f;
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
border-bottom: none;
|
||||||
|
background: var(--q-primary, #1976d2);
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-right.level2-right {
|
||||||
|
grid-column: 7 / 8;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: var(--psq-col-adet);
|
||||||
|
grid-template-rows: 1fr 1fr;
|
||||||
|
align-items: center;
|
||||||
|
justify-items: start;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 0;
|
||||||
|
transform: none !important;
|
||||||
|
border-left: 1px solid #d4c79f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-right .top-total {
|
||||||
|
grid-column: 1 / 2;
|
||||||
|
grid-row: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 1;
|
||||||
|
justify-self: start;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-right .bottom-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-sub-header.level-2 .sub-right .bottom-row {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns:
|
||||||
|
var(--col-model)
|
||||||
|
var(--col-renk)
|
||||||
|
var(--col-ana)
|
||||||
|
var(--col-alt)
|
||||||
|
var(--col-aciklama)
|
||||||
|
calc(var(--grp-title-w) + var(--grp-title-gap) + (var(--beden-w) * var(--beden-count)))
|
||||||
|
var(--psq-col-adet) !important;
|
||||||
|
min-height: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #d4c79f;
|
||||||
|
border-bottom: 1px solid #d4c79f;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell {
|
||||||
|
min-height: 56px;
|
||||||
|
height: 56px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
font-size: 12px;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .grp-area {
|
||||||
|
display: grid !important;
|
||||||
|
grid-template-columns: var(--grp-title-w) var(--grp-title-gap) 1fr;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 56px;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
transform: none !important;
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .grp-row {
|
||||||
|
grid-column: 3;
|
||||||
|
width: 100%;
|
||||||
|
height: 56px;
|
||||||
|
margin-left: 0 !important;
|
||||||
|
justify-content: start !important;
|
||||||
|
display: grid;
|
||||||
|
grid-auto-flow: column;
|
||||||
|
grid-auto-columns: var(--beden-w);
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .grp-row .cell.beden {
|
||||||
|
width: var(--beden-w);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
border-right: 1px solid #d4c79f;
|
||||||
|
border-left: none !important;
|
||||||
|
border-top: none !important;
|
||||||
|
border-bottom: none !important;
|
||||||
|
min-height: 56px;
|
||||||
|
height: 56px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .grp-row .cell.beden:first-child {
|
||||||
|
border-left: 1px solid #d4c79f !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell.model,
|
||||||
|
.order-grid-body .summary-row .cell.renk,
|
||||||
|
.order-grid-body .summary-row .cell.ana,
|
||||||
|
.order-grid-body .summary-row .cell.alt {
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell.aciklama {
|
||||||
|
grid-column: 5 / 6 !important;
|
||||||
|
position: static !important;
|
||||||
|
width: var(--col-aciklama) !important;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
min-height: 56px !important;
|
||||||
|
z-index: auto !important;
|
||||||
|
background: #fff !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
border-right: 1px solid #d4c79f !important;
|
||||||
|
justify-content: flex-start !important;
|
||||||
|
text-align: left !important;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
line-height: 1 !important;
|
||||||
|
padding-left: 6px !important;
|
||||||
|
padding-right: 6px !important;
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: row !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell.depo-merged {
|
||||||
|
grid-column: 1 / 6 !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
text-align: center !important;
|
||||||
|
font-weight: 600;
|
||||||
|
white-space: nowrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body .summary-row .cell.adet,
|
||||||
|
.order-grid-body .summary-row .cell.termin {
|
||||||
|
justify-content: flex-end;
|
||||||
|
text-align: right;
|
||||||
|
padding-right: 10px !important;
|
||||||
|
font-weight: 700;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.order-grid-body > .summary-group,
|
||||||
|
.order-grid-body > .summary-group:first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.detail-table-wrap {
|
||||||
|
padding: 8px 0 12px 0;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-table :deep(th),
|
||||||
|
.detail-table :deep(td) {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -297,6 +297,12 @@ const routes = [
|
|||||||
component: () => import('pages/ProductStockQuery.vue'),
|
component: () => import('pages/ProductStockQuery.vue'),
|
||||||
meta: { permission: 'order:view' }
|
meta: { permission: 'order:view' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'product-stock-by-attributes',
|
||||||
|
name: 'product-stock-by-attributes',
|
||||||
|
component: () => import('pages/ProductStockByAttributes.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/* ================= PASSWORD ================= */
|
/* ================= PASSWORD ================= */
|
||||||
|
|||||||
@@ -2,6 +2,67 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import api from 'src/services/api'
|
import api from 'src/services/api'
|
||||||
|
|
||||||
|
function normalizeTextForMatch (v) {
|
||||||
|
return String(v || '')
|
||||||
|
.trim()
|
||||||
|
.toUpperCase()
|
||||||
|
.normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Production ekranlari icin beden grup tespiti helper'i.
|
||||||
|
// Ozel kural:
|
||||||
|
// YETISKIN/GARSON = GARSON ve URUN ANA GRUBU "GOMLEK ATA YAKA" veya "GOMLEK KLASIK" ise => yas
|
||||||
|
export function detectProductionBedenGroup (bedenList, urunAnaGrubu = '', urunKategori = '', yetiskinGarson = '') {
|
||||||
|
const list = Array.isArray(bedenList) ? bedenList : []
|
||||||
|
const hasLetterSizes = list
|
||||||
|
.map(v => String(v || '').trim().toUpperCase())
|
||||||
|
.some(v => ['XS', 'S', 'M', 'L', 'XL', '2XL', '3XL', '4XL', '5XL', '6XL', '7XL'].includes(v))
|
||||||
|
|
||||||
|
const ana = normalizeTextForMatch(urunAnaGrubu)
|
||||||
|
const kat = normalizeTextForMatch(urunKategori)
|
||||||
|
const yg = normalizeTextForMatch(yetiskinGarson)
|
||||||
|
|
||||||
|
if ((kat.includes('GARSON') || yg.includes('GARSON')) &&
|
||||||
|
(ana.includes('GOMLEK ATAYAKA') || ana.includes('GOMLEK KLASIK'))) {
|
||||||
|
return 'yas'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLetterSizes) return 'gom'
|
||||||
|
if ((ana.includes('AYAKKABI') || kat.includes('AYAKKABI')) && (kat.includes('GARSON') || yg.includes('GARSON'))) return 'ayk_garson'
|
||||||
|
if (kat.includes('GARSON') || yg.includes('GARSON') || ana.includes('GARSON')) return 'yas'
|
||||||
|
if (ana.includes('PANTOLON') && kat.includes('YETISKIN')) return 'pan'
|
||||||
|
if (ana.includes('AKSESUAR')) return 'aksbir'
|
||||||
|
return 'tak'
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractApiErrorMessage (err, fallback) {
|
||||||
|
const data = err?.response?.data
|
||||||
|
if (typeof data === 'string' && data.trim()) return data
|
||||||
|
if (data && typeof data === 'object') {
|
||||||
|
const msg = String(data.message || '').trim()
|
||||||
|
const step = String(data.step || '').trim()
|
||||||
|
const detail = String(data.detail || '').trim()
|
||||||
|
const parts = [msg]
|
||||||
|
if (step) parts.push(`step=${step}`)
|
||||||
|
if (detail) parts.push(detail)
|
||||||
|
const merged = parts.filter(Boolean).join(' | ')
|
||||||
|
if (merged) return merged
|
||||||
|
}
|
||||||
|
return err?.message || fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
function logApiError (action, err, payload = null) {
|
||||||
|
const status = err?.response?.status
|
||||||
|
const data = err?.response?.data
|
||||||
|
console.error(`[OrderProductionItemStore] ${action} failed`, {
|
||||||
|
status,
|
||||||
|
payload,
|
||||||
|
data,
|
||||||
|
message: err?.message
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
items: [],
|
items: [],
|
||||||
@@ -118,7 +179,8 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
|||||||
)
|
)
|
||||||
return res?.data || { missingCount: 0, missing: [] }
|
return res?.data || { missingCount: 0, missing: [] }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error = err?.response?.data || err?.message || 'Kontrol basarisiz'
|
logApiError('validateUpdates', err, { orderHeaderID, lineCount: lines?.length || 0 })
|
||||||
|
this.error = extractApiErrorMessage(err, 'Kontrol basarisiz')
|
||||||
throw err
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
this.saving = false
|
this.saving = false
|
||||||
@@ -137,7 +199,8 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
|||||||
)
|
)
|
||||||
return res?.data || { updated: 0, inserted: 0 }
|
return res?.data || { updated: 0, inserted: 0 }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.error = err?.response?.data || err?.message || 'Guncelleme basarisiz'
|
logApiError('applyUpdates', err, { orderHeaderID, lineCount: lines?.length || 0, insertMissing })
|
||||||
|
this.error = extractApiErrorMessage(err, 'Guncelleme basarisiz')
|
||||||
throw err
|
throw err
|
||||||
} finally {
|
} finally {
|
||||||
this.saving = false
|
this.saving = false
|
||||||
|
|||||||
@@ -3438,17 +3438,28 @@ export function normalizeBeden(v) {
|
|||||||
- Core logic aligned with backend detectBedenGroupGo
|
- Core logic aligned with backend detectBedenGroupGo
|
||||||
- Keeps frontend aksbir bucket for accessory lines
|
- Keeps frontend aksbir bucket for accessory lines
|
||||||
=========================================================== */
|
=========================================================== */
|
||||||
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '') {
|
export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = '', yetiskinGarson = '') {
|
||||||
const list = Array.isArray(bedenList) && bedenList.length > 0
|
const list = Array.isArray(bedenList) && bedenList.length > 0
|
||||||
? bedenList.map(v => (v || '').toString().trim().toUpperCase())
|
? bedenList.map(v => (v || '').toString().trim().toUpperCase())
|
||||||
: [' ']
|
: [' ']
|
||||||
|
|
||||||
const rawAna = (urunAnaGrubu || '').toString().toUpperCase()
|
const rawAna = normalizeTextForMatch(urunAnaGrubu || '')
|
||||||
const rawKat = (urunKategori || '').toString().toUpperCase()
|
const rawKat = normalizeTextForMatch(urunKategori || '')
|
||||||
const hasGarson = rawAna.includes('GARSON') || rawKat.includes('GARSON') ||
|
const rawYetiskinGarson = normalizeTextForMatch(yetiskinGarson || '')
|
||||||
rawAna.includes('GARSON') || rawKat.includes('GARSON')
|
|
||||||
const hasAyakkabi = rawAna.includes('AYAKKABI') || rawKat.includes('AYAKKABI') ||
|
// Ozel kural:
|
||||||
rawAna.includes('AYAKKABI') || rawKat.includes('AYAKKABI')
|
// YETISKIN/GARSON = GARSON ve URUN ANA GRUBU "GOMLEK ATA YAKA" veya "GOMLEK KLASIK" ise
|
||||||
|
// sonuc "yas" olmalidir.
|
||||||
|
const isGarsonGomlekAnaGrubu =
|
||||||
|
rawAna.includes('GOMLEK ATA YAKA') ||
|
||||||
|
rawAna.includes('GOMLEK KLASIK')
|
||||||
|
const hasGarsonSignal = rawAna.includes('GARSON') || rawKat.includes('GARSON') || rawYetiskinGarson.includes('GARSON')
|
||||||
|
if (isGarsonGomlekAnaGrubu && (rawKat.includes('GARSON') || rawYetiskinGarson.includes('GARSON'))) {
|
||||||
|
return 'yas'
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasGarson = hasGarsonSignal
|
||||||
|
const hasAyakkabi = rawAna.includes('AYAKKABI') || rawKat.includes('AYAKKABI')
|
||||||
if (hasGarson && hasAyakkabi) return 'ayk_garson'
|
if (hasGarson && hasAyakkabi) return 'ayk_garson'
|
||||||
if (hasGarson) return 'yas'
|
if (hasGarson) return 'yas'
|
||||||
|
|
||||||
@@ -3457,28 +3468,27 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
|
|||||||
const harfliBedenler = ['XS','S','M','L','XL','2XL','3XL','4XL','5XL','6XL','7XL']
|
const harfliBedenler = ['XS','S','M','L','XL','2XL','3XL','4XL','5XL','6XL','7XL']
|
||||||
if (list.some(b => harfliBedenler.includes(b))) return 'gom'
|
if (list.some(b => harfliBedenler.includes(b))) return 'gom'
|
||||||
|
|
||||||
const ana = (urunAnaGrubu || '')
|
const ana = normalizeTextForMatch(urunAnaGrubu || '')
|
||||||
.toUpperCase()
|
|
||||||
.trim()
|
.trim()
|
||||||
.replace(/\(.*?\)/g, '')
|
.replace(/\(.*?\)/g, '')
|
||||||
.replace(/[^A-ZÇĞİÖŞÜ0-9\s]/g, '')
|
.replace(/[^A-Z0-9\s]/g, '')
|
||||||
.replace(/\s+/g, ' ')
|
.replace(/\s+/g, ' ')
|
||||||
|
|
||||||
const kat = (urunKategori || '').toUpperCase().trim()
|
const kat = normalizeTextForMatch(urunKategori || '').trim()
|
||||||
// 🔸 Aksesuar ise "aksbir"
|
// 🔸 Aksesuar ise "aksbir"
|
||||||
const aksesuarGruplari = [
|
const aksesuarGruplari = [
|
||||||
'AKSESUAR','KRAVAT','PAPYON','KEMER','CORAP','ÇORAP',
|
'AKSESUAR','KRAVAT','PAPYON','KEMER','CORAP',
|
||||||
'FULAR','MENDIL','MENDİL','KASKOL','ASKI',
|
'FULAR','MENDIL','KASKOL','ASKI',
|
||||||
'YAKA','KOL DUGMESI','KOL DÜĞMESİ'
|
'YAKA','KOL DUGMESI'
|
||||||
]
|
]
|
||||||
const giyimGruplari = ['GÖMLEK','CEKET','PANTOLON','MONT','YELEK','TAKIM','TSHIRT','TİŞÖRT']
|
const giyimGruplari = ['GOMLEK','CEKET','PANTOLON','MONT','YELEK','TAKIM','TSHIRT','TISORT']
|
||||||
// 🔸 Pantolon özel durumu
|
// 🔸 Pantolon özel durumu
|
||||||
if (
|
if (
|
||||||
aksesuarGruplari.some(g => ana.includes(g) || kat.includes(g)) &&
|
aksesuarGruplari.some(g => ana.includes(g) || kat.includes(g)) &&
|
||||||
!giyimGruplari.some(g => ana.includes(g))
|
!giyimGruplari.some(g => ana.includes(g))
|
||||||
) return 'aksbir'
|
) return 'aksbir'
|
||||||
|
|
||||||
if (ana.includes('PANTOLON') && kat.includes('YETİŞKİN')) return 'pan'
|
if (ana.includes('PANTOLON') && kat.includes('YETISKIN')) return 'pan'
|
||||||
// 🔸 Tamamen numerik (örneğin 39-44 arası) → ayakkabı
|
// 🔸 Tamamen numerik (örneğin 39-44 arası) → ayakkabı
|
||||||
const allNumeric = list.every(v => /^\d+$/.test(v))
|
const allNumeric = list.every(v => /^\d+$/.test(v))
|
||||||
if (allNumeric) {
|
if (allNumeric) {
|
||||||
@@ -3488,7 +3498,7 @@ export function detectBedenGroup(bedenList, urunAnaGrubu = '', urunKategori = ''
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 🔸 Yaş grubu (çocuk/garson)
|
// 🔸 Yaş grubu (çocuk/garson)
|
||||||
if (kat.includes('GARSON') || kat.includes('ÇOCUK')) return 'yas'
|
if (kat.includes('GARSON') || kat.includes('COCUK')) return 'yas'
|
||||||
|
|
||||||
// 🔸 Varsayılan: takım elbise
|
// 🔸 Varsayılan: takım elbise
|
||||||
return 'tak'
|
return 'tak'
|
||||||
|
|||||||
Reference in New Issue
Block a user