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
// ELLO Textile XML -> Shopify CSV Converter module.exports = function productToShopifyRows(item, utils = {}) { // ---------- Helper Functions ---------- const trim = v => (v == null ? '' : String(v).trim()); const pick = (...vals) => vals.find(v => v !== undefined && v !== null && String(v).trim() !== ''); const isUrl = u => /^https?:\/\//i.test(String(u || '')); const toNum = v => { if (v == null || v === '') return null; const n = Number(String(v).replace(',', '.')); return Number.isFinite(n) ? n : null; }; const money = v => { const n = toNum(v); return n == null ? '0.00' : n.toFixed(2); }; const uniq = arr => Array.from(new Set((arr || []).filter(Boolean))); const slugify = (s = '') => (utils.slugify ? utils.slugify(s) : String(s).toLowerCase().normalize('NFKD') .replace(/[\u0300-\u036f]/g, '') .replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '') ); const minifyHTML = (s = '') => String(s) .replace(/\s+/g, ' ') .replace(/\s*(>|\<)\s*/g, '$1') .trim(); // xml2js flexibility helper - array'i unwrap eder ama obje yapısını korur const val = v => { if (Array.isArray(v)) return val(v[0]); // Obje içinde sadece `_` varsa ve $ yoksa text content döndür if (v && typeof v === 'object' && '_' in v && !('$' in v)) return val(v._); return v; }; const toArray = v => v == null ? [] : (Array.isArray(v) ? v : [v]); // Beden sırası (STD ve birleşik bedenler dahil) const SIZE_ORDER = ['XXS', 'XS', 'S', 'S-M', 'SM', 'M', 'M-L', 'ML', 'L', 'L-XL', 'LXL', 'XL', '2XL', '3XL', '4XL', '5XL', 'STD', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '36', '38', '40', '42', '44', '46', '48', '50']; const sizeRank = s => { const u = String(s || '').toUpperCase().replace(/\s/g, ''); const i = SIZE_ORDER.findIndex(size => size.replace(/\s/g, '') === u); return i === -1 ? 999 : i; }; // ---------- Parse OzelAlan1 (Set İçerik Bilgisi) ---------- const parseSetInfo = (ozelAlan1) => { const text = trim(val(ozelAlan1)); if (!text) return ''; const parts = []; // Format 1: "2S 2M 2L" → "S-2 M-2 L-2" const format1 = text.match(/(\d+)([A-Z]+(?:-[A-Z]+)?)/gi); if (format1) { format1.forEach(part => { const match = part.match(/^(\d+)([A-Z]+(?:-[A-Z]+)?)$/i); if (match) { const qty = match[1]; const size = match[2].toUpperCase(); parts.push(`${size}-${qty}`); } }); } // Format 2: "1*26 2*27" → "26-1 27-2" const format2 = text.match(/(\d+)\*(\d+)/g); if (format2) { format2.forEach(part => { const match = part.match(/^(\d+)\*(\d+)$/); if (match) { const qty = match[1]; const size = match[2]; parts.push(`${size}-${qty}`); } }); } // Format 3: "3 S-M 3 M-L" → "S-M-3 M-L-3" const format3 = text.match(/(\d+)\s+([A-Z]+-[A-Z]+)/gi); if (format3) { format3.forEach(part => { const match = part.match(/^(\d+)\s+([A-Z]+-[A-Z]+)$/i); if (match) { const qty = match[1]; const size = match[2].toUpperCase(); parts.push(`${size}-${qty}`); } }); } if (parts.length === 0) { // Fallback: olduğu gibi döndür return text ? `Set İçerik Bilgisi: ${text}` : ''; } return `<p><strong>Set İçerik Bilgisi:</strong> ${parts.join(' ')}</p>`; }; // ---------- Product-Level Fields ---------- const urunId = trim(val(item.UrunKartiID)); const urunAdi = trim(val(item.UrunAdi)); const marka = trim(val(item.Marka)); const kategori = trim(val(item.Kategori)); const kategoriTree = trim(val(item.KategoriTree)); const onYazi = trim(val(item.OnYazi)); const aciklama = trim(val(item.Aciklama)); const ozelAlan1 = trim(val(item.OzelAlan1)); // Set içerik bilgisi oluştur const setInfo = parseSetInfo(ozelAlan1); // Body HTML - Set bilgisini EN BAŞA ekle let bodyHTML = ''; if (setInfo) { bodyHTML = setInfo; } if (aciklama) { bodyHTML += bodyHTML ? '\n' + aciklama : aciklama; } if (onYazi && onYazi !== aciklama) { bodyHTML += bodyHTML ? '\n<p>' + onYazi + '</p>' : '<p>' + onYazi + '</p>'; } bodyHTML = minifyHTML(bodyHTML); // Tags: Marka + Kategori ağacı const categoryParts = kategoriTree.split('/').map(s => trim(s)).filter(Boolean); const tags = uniq([marka, ...categoryParts]).join(', '); // Handle oluştur const Handle = slugify([marka, urunAdi, urunId].filter(Boolean).join(' ')) || `product-${urunId}`; // Resimleri al const images = []; let resimlerRoot = val(item.Resimler); if (resimlerRoot) { const resimArray = toArray(resimlerRoot.Resim || resimlerRoot); resimArray.forEach(r => { const url = trim(val(r)); if (isUrl(url)) { images.push(url); } }); } // Varyantları parse et const variants = []; // xml2js explicitArray: true kullanıyor, önce array'den çıkar let seceneklerRoot = val(item.UrunSecenek); if (seceneklerRoot) { let secenekArray = toArray(seceneklerRoot.Secenek || seceneklerRoot); secenekArray.forEach(secenek => { const varyasyonId = trim(val(secenek.VaryasyonID)); const stokKodu = trim(val(secenek.StokKodu)); const barkod = trim(val(secenek.Barkod)); const stokAdedi = trim(val(secenek.StokAdedi)); const satisFiyati = val(secenek.SatisFiyati); const indirimliFiyat = val(secenek.IndirimliFiyat); const kdvDahil = String(val(secenek.KDVDahil)).toLowerCase() === 'true'; const kdvOrani = toNum(val(secenek.KdvOrani)) || 0; // Fiyat hesapla (KDV dahil değilse ekle) let finalPrice = toNum(indirimliFiyat) || toNum(satisFiyati) || 0; let comparePrice = toNum(satisFiyati) || 0; if (!kdvDahil && kdvOrani > 0) { const kdvMultiplier = 1 + (kdvOrani / 100); finalPrice = finalPrice * kdvMultiplier; comparePrice = comparePrice * kdvMultiplier; } // Renk ve Beden bilgilerini al let renk = ''; let beden = ''; // xml2js: EkSecenekOzellik bir array, içinde { Ozellik: [...] } var let ozelliklerRoot = val(secenek.EkSecenekOzellik); // Array'den çıkar if (ozelliklerRoot && ozelliklerRoot.Ozellik) { const ozellikArray = toArray(ozelliklerRoot.Ozellik); ozellikArray.forEach((oz, index) => { // xml2js ile parse edilen obje - val() artık objeyi korur const ozObj = val(oz); // Text content let textContent = ''; if (ozObj && typeof ozObj === 'object' && ozObj._) { textContent = trim(String(ozObj._)); } else if (typeof ozObj === 'string') { textContent = trim(ozObj); } // Attributes parse (Tanim ve Deger) let tanim = ''; let deger = textContent; // Default: text content if (ozObj && typeof ozObj === 'object' && ozObj.$) { // xml2js standard: attributes $ altında if (ozObj.$.Tanim) { tanim = trim(String(ozObj.$.Tanim)); } if (ozObj.$.Deger) { deger = trim(String(ozObj.$.Deger)); } } // Renk ve Beden eşleme if (tanim) { const tanimLower = tanim.toLowerCase(); if (tanimLower.includes('renk') || tanimLower.includes('color')) { renk = deger; } else if (tanimLower.includes('beden') || tanimLower.includes('size')) { beden = deger; } } // Fallback: Sıralama ile eşle (renk genelde ilk, beden ikinci) else if (index === 0 && deger) { renk = deger; } else if (index === 1 && deger) { beden = deger; } }); } variants.push({ varyasyonId, stokKodu, barkod, stokAdedi, finalPrice, comparePrice, renk: String(renk || '').trim(), beden: String(beden || '').trim() }); }); } // Beden sırasına göre sırala variants.sort((a, b) => sizeRank(a.beden) - sizeRank(b.beden)); // ---------- Shopify CSV Rows Oluştur ---------- const rows = []; // Base template - İLK SATIR için tam veri const baseFull = { __PRODUCT_KEY: urunId, // Diff için Handle, Title: urunAdi || `Product ${urunId}`, 'Body (HTML)': bodyHTML, Vendor: marka || '', Type: kategori || '', Tags: tags, Published: 'TRUE', 'Option1 Name': variants.length > 0 && variants.some(v => v.beden) ? 'Beden' : '', 'Option2 Name': variants.length > 0 && variants.some(v => v.renk) ? 'Renk' : '', 'Option3 Name': '', 'Variant Inventory Tracker': 'shopify', 'Variant Inventory Policy': 'deny', 'Variant Fulfillment Service': 'manual', 'Variant Requires Shipping': 'TRUE', 'Variant Taxable': 'TRUE', 'Gift Card': '', 'SEO Title': urunAdi || '', 'SEO Description': onYazi || '', Status: 'active' }; // Base template - SONRAKI SATIRLAR için minimal veri const baseLite = { __PRODUCT_KEY: urunId, // Diff için Handle, Title: '', 'Body (HTML)': '', Vendor: '', Type: '', Tags: '', Published: '', 'Option1 Name': '', 'Option2 Name': '', 'Option3 Name': '', 'Variant Inventory Tracker': 'shopify', 'Variant Inventory Policy': 'deny', 'Variant Fulfillment Service': 'manual', 'Variant Requires Shipping': 'TRUE', 'Variant Taxable': 'TRUE', 'Gift Card': '', 'SEO Title': '', 'SEO Description': '', Status: '' }; if (variants.length === 0) { // Varyant yok - tek ürün const row = { ...baseFull }; row['Option1 Name'] = ''; row['Option2 Name'] = ''; row['Variant SKU'] = trim(val(item.StokKodu)) || urunId; row['Variant Barcode'] = trim(val(item.Barkod)) || ''; row['Variant Price'] = '0.00'; row['Variant Compare At Price'] = ''; row['Variant Inventory Qty'] = '0'; if (images.length > 0) { row['Image Src'] = images[0]; row['Image Position'] = '1'; row['Image Alt Text'] = urunAdi; } rows.push(row); } else { // Çoklu varyant variants.forEach((variant, index) => { const base = index === 0 ? baseFull : baseLite; const row = { ...base, 'Option1 Value': String(variant.beden || '').trim(), 'Option2 Value': String(variant.renk || '').trim(), 'Variant SKU': variant.stokKodu || `${urunId}-${variant.varyasyonId}`, 'Variant Barcode': variant.barkod || '', 'Variant Price': money(variant.finalPrice), 'Variant Compare At Price': variant.comparePrice > variant.finalPrice ? money(variant.comparePrice) : '', 'Variant Inventory Qty': variant.stokAdedi || '0' }; // İlk varyanta ilk resmi ekle if (index === 0 && images.length > 0) { row['Image Src'] = images[0]; row['Image Position'] = '1'; row['Image Alt Text'] = urunAdi; } rows.push(row); }); } // Ekstra resimleri ekle (ilk resim zaten eklendi) if (images.length > 1) { images.slice(1).forEach((imgUrl, i) => { rows.push({ __PRODUCT_KEY: urunId, // Diff için Handle, 'Image Src': imgUrl, 'Image Position': String(i + 2), 'Image Alt Text': urunAdi }); }); } return rows; };
Güncelle
İptal
İstatistikler
Toplam Çalışma
2209
Başarı Oranı
98%
İşlenen Ürün
0
Push Edilen
0