E-Ticaret Panel
Mağazalar
Mağaza Düzenle
Mağaza Adı
XML Linki
Shopify Store Name
.myshopify.com
Access Token
API Version
2025-07 (En Güncel - Önerilen)
2025-04
2025-01
2024-10
2024-07
2024-04 (Legacy)
2024-01 (Legacy)
Mevcut: 2025-07
Kontrol Süresi
Saniye
Dakika
Saat
Gün
Product Path (Opsiyonel)
XML'de ürünlerin bulunduğu path
🏷️ Vendor (Marka) Filtreleme
📦 Tüm markalar işleniyor
← Ana sayfadan "Filtre Ekle" butonuyla vendor seçimi yapabilirsiniz
Converter Kodunuz
module.exports = function convert(root, utils = {}) { /* ── ERKEN ÇIKIŞLAR ─────────────────── */ if (!root) return []; /* <products>, <items> veya <root><item> listeleri */ if (root.products?.product) { const list = Array.isArray(root.products.product) ? root.products.product : [root.products.product]; return list.flatMap(p => convert(p, utils)); } if (root.items?.item) { const list = Array.isArray(root.items.item) ? root.items.item : [root.items.item]; return list.flatMap(p => convert(p, utils)); } if (root.item) { const list = Array.isArray(root.item) ? root.item : [root.item]; return list.flatMap(p => convert(p, utils)); } /* Bu noktada root tek bir ürün düğümü */ const p = root; /* ── YARDIMCILAR ─────────────────────── */ const get = v => { if (v == null) return ''; if (Array.isArray(v)) return get(v[0]); if (typeof v === 'object') { // CDATA içeriklerini handle et if ('_' in v) return get(v._); if ('#text' in v) return get(v['#text']); if ('$' in v && v.$ && typeof v.$ === 'string') return v.$.trim(); // Eğer tek bir property varsa ve string ise onu al const values = Object.values(v); if (values.length === 1 && typeof values[0] === 'string') { return values[0].trim(); } const first = values[0]; return get(first); } return String(v).trim(); }; const toPrice = v => { const n = Number(String(v || '').replace(',', '.')); return Number.isFinite(n) ? n.toFixed(2) : '0.00'; }; const cleanUrl = url => { const cleaned = String(url || '').trim(); return /^https?:\/\//i.test(cleaned) ? cleaned : ''; }; const uniq = arr => [...new Set(arr.filter(Boolean))]; const collectImages = obj => { if (!obj || typeof obj !== 'object') return []; return Object.keys(obj) .filter(k => /^(v?Picture|picture|image|img).*(Path)?\d*$/i.test(k)) .map(k => cleanUrl(get(obj[k]))) .filter(Boolean); }; const slugify = str => String(str).toLowerCase() .replace(/[ğĞ]/g,'g').replace(/[üÜ]/g,'u') .replace(/[şŞ]/g,'s').replace(/[ıİ]/g,'i') .replace(/[öÖ]/g,'o').replace(/[çÇ]/g,'c') .replace(/[^a-z0-9]+/g,'-').replace(/^-+|-+$/g,''); // Size tablosunu parse et - BENZERSIZ bedenler ve miktarları const parseSizeInfo = (htmlContent) => { if (!htmlContent) return [{ size: 'STD', qty: 1 }]; try { // HTML decode const decoded = htmlContent.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); // Bedenler: S, M, L, XL const sizePattern = /<td[^>]*>\s*(S|M|L|XL|XXL)\s*<\/td>/gi; const qtyPattern = /<td[^>]*>\s*(\d+)\s*<\/td>/g; const sizes = []; const quantities = []; let match; while ((match = sizePattern.exec(decoded)) !== null) { sizes.push(match[1].toUpperCase()); } while ((match = qtyPattern.exec(decoded)) !== null) { quantities.push(parseInt(match[1], 10)); } // Eğer size ve quantity eşleşiyorsa - BENZERSIZ bedenler döndür if (sizes.length === quantities.length && sizes.length > 0) { const sizeInfo = []; for (let i = 0; i < sizes.length; i++) { sizeInfo.push({ size: sizes[i], qty: quantities[i] // Toplam miktar her beden için }); } return sizeInfo.length > 0 ? sizeInfo : [{ size: 'STD', qty: 1 }]; } } catch (e) { console.warn('Size parsing error:', e); } return [{ size: 'STD', qty: 1 }]; // Varsayılan beden }; /* ── TEMEL ALANLAR ───────────────────── */ const code = get(p.stockCode) || get(p.code) || 'NO-CODE'; const title = get(p.label) || get(p.name) || 'Adsız Ürün'; const vendor = get(p.brand) || 'missJANELL'; const type = get(p.category) || ''; const subCategory = get(p.subCategory) || ''; const mainCategory = get(p.mainCategory) || ''; const tags = [mainCategory, type, subCategory] .filter(Boolean) .join(', '); const bodyHtml = get(p.details) || ''; const seoTitle = title; const seoDesc = ''; const totalStock = Math.max(0, parseInt(get(p.stockAmount) || '0', 10)); const basePrice = toPrice(get(p.price1) || '0'); const weightG = 200; // Varsayılan ağırlık /* ÜRÜN GÖRSELLERİ */ const mainImages = collectImages(p); /* SIZE BİLGİSİ */ const sizeInfo = parseSizeInfo(get(p.productSpecialInfoContent)); const hasSizes = sizeInfo.length > 1 || (sizeInfo[0] && sizeInfo[0].size !== 'STD'); /* ── VARYANTLAR ──────────────────────── */ const rawVariants = p.variants?.variant ? (Array.isArray(p.variants.variant) ? p.variants.variant : [p.variants.variant]) : []; // Eğer varyantlar bulunamıyorsa, XML'deki vStockCode pattern'lerini ara if (rawVariants.length === 0) { const xmlStr = JSON.stringify(p); const vStockPattern = new RegExp(`${code}\\d{3}`, 'g'); const matches = xmlStr.match(vStockPattern); if (matches && matches.length > 0) { // Her vStockCode için renk çıkar ve varyant oluştur const uniqueMatches = [...new Set(matches)]; // Duplikatları temizle uniqueMatches.forEach(vStock => { let color = ''; if (vStock.endsWith('001')) color = 'SİYAH'; else if (vStock.endsWith('012')) color = 'MAVİ'; else if (vStock.endsWith('015')) color = 'PEMBE'; else if (vStock.endsWith('026')) color = 'TAŞ'; else if (vStock.endsWith('032')) color = 'ZÜMRÜT YEŞİLİ'; else if (vStock.endsWith('036')) color = 'BORDO'; else if (vStock.endsWith('040')) color = 'KIRMIZI'; else if (vStock.endsWith('044')) color = 'BEYAZ'; else if (vStock.endsWith('048')) color = 'SARI'; else if (vStock.endsWith('052')) color = 'YEŞİL'; else color = 'Renk-' + vStock.slice(-3); // XML'den gerçek stok miktarını çıkarmaya çalış const stockPattern = new RegExp(`"vStockCode"[^}]*"${vStock}"[^}]*"vStockAmount"[^}]*"(\\d+)"`, 'gi'); const stockMatch = xmlStr.match(stockPattern); let stockAmount = Math.floor(totalStock / uniqueMatches.length); if (stockMatch && stockMatch[0]) { const stockValue = stockMatch[0].match(/"vStockAmount"[^}]*"(\d+)"/); if (stockValue && stockValue[1]) { const parsedStock = parseInt(stockValue[1], 10); // Stok miktarını sınırla (max 9999) if (parsedStock > 0 && parsedStock < 10000) { stockAmount = parsedStock; } } } // Güvenlik kontrolü - stok çok yüksekse sıfırla if (stockAmount > 9999 || isNaN(stockAmount)) { stockAmount = Math.floor(totalStock / uniqueMatches.length); } // Final stok sınırlaması stockAmount = Math.min(Math.max(stockAmount, 1), 999); rawVariants.push({ vStockCode: vStock, vStockAmount: stockAmount.toString(), vPrice1: basePrice, vBarcode: '', options: { option: { variantName: 'Renk', variantValue: color } } }); }); } } let variants = []; // DEBUG: Varyant yapısını kontrol et console.log(`\n=== ÜRÜN: ${code} - ${title} ===`); console.log('Raw variants length:', rawVariants.length); console.log('Variants structure:', JSON.stringify(p.variants, null, 2)); if (rawVariants.length > 0) { // Renk varyantları var rawVariants.forEach((v, idx) => { let color = ''; console.log(`\nVariant ${idx}:`, JSON.stringify(v, null, 2)); // Renk bilgisini çıkar if (v.options?.option) { const opts = Array.isArray(v.options.option) ? v.options.option : [v.options.option]; console.log('Options found:', opts.length); opts.forEach(o => { const name = get(o.variantName).toLowerCase(); const val = get(o.variantValue); console.log(`Option - Name: "${name}", Value: "${val}"`); if (name.includes('renk') || name.includes('color')) { color = val; } }); } else { console.log('No options found in variant'); } // Eğer renk bulunamadıysa SKU'dan çıkarmaya çalış if (!color) { const sku = get(v.vStockCode); // SKU pattern: 1001001 (SİYAH), 1001012 (MAVİ), 1001015 (PEMBE) if (sku.endsWith('001')) color = 'SİYAH'; else if (sku.endsWith('012')) color = 'MAVİ'; else if (sku.endsWith('015')) color = 'PEMBE'; else if (sku.endsWith('026')) color = 'TAŞ'; else if (sku.endsWith('032')) color = 'ZÜMRÜT YEŞİLİ'; } console.log(`\nVariant ${idx}:`, { vStockCode: get(v.vStockCode), vStockAmount: get(v.vStockAmount), vBarcode: get(v.vBarcode), rawVStockAmount: v.vStockAmount, rawVBarcode: v.vBarcode }); const variantImages = collectImages(v); const vSku = get(v.vStockCode) || `${code}-${color}`; const vBarcode = get(v.vBarcode) || ''; // ÇÖZÜM: XML parser field karışıklığı için alternatif yaklaşım let vQty = 0; // Method 1: Direkt field'dan çek const stockMethod1 = get(v.vStockAmount); // Method 2: XML string'den regex ile çek const xmlStr = JSON.stringify(v); const stockRegex = /"vStockAmount"[^}]*"(\d{1,4})"/; // 1-4 haneli sayı (stok) const stockMatch = xmlStr.match(stockRegex); const stockMethod2 = stockMatch ? stockMatch[1] : null; // Method 3: Küçük sayıları tercih et (stok genelde küçük, barcode büyük) const allNumbers = xmlStr.match(/\d+/g) || []; const smallNumbers = allNumbers.filter(n => parseInt(n, 10) < 1000 && parseInt(n, 10) > 0); const stockMethod3 = smallNumbers[0] || null; console.log(`Stock methods - M1: ${stockMethod1}, M2: ${stockMethod2}, M3: ${stockMethod3}`); // En mantıklı değeri seç if (stockMethod2 && parseInt(stockMethod2, 10) < 1000) { vQty = parseInt(stockMethod2, 10); } else if (stockMethod3 && parseInt(stockMethod3, 10) < 1000) { vQty = parseInt(stockMethod3, 10); } else { const parsed = parseInt(stockMethod1, 10); vQty = (parsed < 1000 && parsed > 0) ? parsed : Math.floor(totalStock / rawVariants.length); } console.log(`Final stock amount: ${vQty}`); const vPrice = toPrice(get(v.vPrice1) || basePrice); if (hasSizes && sizeInfo.length > 0) { // Her renk için her bedeni ekle - BENZERSIZ varyantlar sizeInfo.forEach(sizeData => { const sizeStockRatio = sizeData.qty / sizeInfo.reduce((sum, s) => sum + s.qty, 0); const sizeQty = Math.floor(vQty * sizeStockRatio); const finalQty = Math.min(Math.max(sizeQty, 1), 999); variants.push({ color: color || 'Tek Renk', size: sizeData.size, sku: `${vSku}-${sizeData.size}`, barcode: vBarcode, qty: finalQty, price: vPrice, images: variantImages }); }); } else { // Sadece renk varyantı const finalQty = Math.min(Math.max(vQty, 1), 999); // 1-999 arası sınırla variants.push({ color: color || 'Tek Renk', size: '', sku: vSku, barcode: vBarcode, qty: finalQty, price: vPrice, images: variantImages }); } }); } else { // Varyant yok, sadece ana ürün if (hasSizes && sizeInfo.length > 0) { sizeInfo.forEach(sizeData => { const sizeStockRatio = sizeData.qty / sizeInfo.reduce((sum, s) => sum + s.qty, 0); const sizeQty = Math.floor(totalStock * sizeStockRatio); const finalQty = Math.min(Math.max(sizeQty, 1), 999); variants.push({ color: 'Tek Renk', size: sizeData.size, sku: `${code}-${sizeData.size}`, barcode: get(p.barcode) || '', qty: finalQty, price: basePrice, images: [] }); }); } else { const finalQty = Math.min(Math.max(totalStock, 1), 999); variants.push({ color: 'Tek Renk', size: '', sku: code, barcode: get(p.barcode) || '', qty: finalQty, price: basePrice, images: [] }); } } /* Shopify CSV SATIRLARI */ const productHandle = slugify(`${code}-${title}`); const rows = []; let imgPos = 1; const baseRow = () => ({ Handle: productHandle, Title: '', 'Body (HTML)': '', Vendor: '', Type: '', Tags: '', Published: 'TRUE', 'Option1 Name': 'Renk', 'Option1 Value': '', 'Option2 Name': hasSizes ? 'Beden' : '', 'Option2 Value': '', 'Option3 Name': '', 'Option3 Value': '', 'Variant SKU': '', 'Variant Grams': weightG, 'Variant Inventory Tracker': 'shopify', 'Variant Inventory Qty': 0, 'Variant Inventory Policy': 'deny', 'Variant Fulfillment Service': 'manual', 'Variant Price': '0.00', 'Variant Compare At Price': '', 'Variant Requires Shipping': 'TRUE', 'Variant Taxable': 'TRUE', 'Variant Barcode': '', 'Image Src': '', 'Image Position': '', 'Image Alt Text': '', 'Gift Card': 'FALSE', 'SEO Title': seoTitle, 'SEO Description': seoDesc, 'Google Shopping / Google Product Category': '', 'Google Shopping / Gender': 'female', 'Google Shopping / Age Group': 'adult', 'Google Shopping / MPN': '', 'Google Shopping / AdWords Grouping': '', 'Google Shopping / AdWords Labels': '', 'Google Shopping / Condition': 'new', 'Google Shopping / Custom Product': '', 'Google Shopping / Custom Label 0': '', 'Google Shopping / Custom Label 1': '', 'Google Shopping / Custom Label 2': '', 'Google Shopping / Custom Label 3': '', 'Google Shopping / Custom Label 4': '', 'Variant Image': '', 'Variant Weight Unit': 'g', 'Variant Tax Code': '', 'Cost per item': '', Status: 'active' }); /* VARYANT SATIRLARI */ variants.forEach((v, idx) => { const r = baseRow(); // İlk satırda ürün bilgilerini ekle if (idx === 0) { r.Title = title; r['Body (HTML)'] = bodyHtml; r.Type = type; r.Tags = tags; r.Vendor = vendor; } r['Option1 Value'] = v.color; r['Option2 Value'] = hasSizes ? v.size : ''; r['Variant SKU'] = v.sku; r['Variant Barcode'] = v.barcode; r['Variant Inventory Qty'] = v.qty; r['Variant Price'] = v.price; // Varyant görseli const variantImg = v.images[0] || mainImages[0] || ''; if (variantImg) { r['Variant Image'] = variantImg; } // İlk satırda ana görseli ekle if (idx === 0 && (variantImg || mainImages[0])) { r['Image Src'] = variantImg || mainImages[0]; r['Image Position'] = imgPos++; r['Image Alt Text'] = `${title} ${v.color}`.trim(); } rows.push(r); }); /* EK ÜRÜN GÖRSELLERİ */ const allImages = uniq([ ...variants.flatMap(v => v.images), ...mainImages ]); // İlk görsel zaten eklenmiş, geri kalanları ekle allImages.slice(1).forEach(src => { const r = baseRow(); // Görsel satırlarının gereksiz alanlarını temizle r.Published = ''; r.Status = ''; r['Option1 Name'] = ''; r['Option1 Value'] = ''; r['Option2 Name'] = ''; r['Option2 Value'] = ''; r['Image Src'] = src; r['Image Position'] = imgPos++; r['Image Alt Text'] = title; rows.push(r); }); return rows; };
Güncelle
İptal
İstatistikler
Toplam Çalışma
4385
Başarı Oranı
82%
İşlenen Ürün
0
Push Edilen
0