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
// Vienetta Wholesale XML -> Shopify CSV Converter // URL: https://patronundan.com/vienetta-xml-ws module.exports = function vienettaWSConverter(item, utils = {}) { // ========== HELPER FUNCTIONS ========== const one = (v) => Array.isArray(v) ? v[0] : v; const txt = (v) => (v && typeof v === 'object' && '_' in v) ? v._ : v; const get = (k, d = '') => { const val = txt(one(item[k])); return (val === undefined || val === null) ? d : String(val).trim(); }; const validUrl = (u) => { const url = String(u || '').trim(); return url.startsWith('http://') || url.startsWith('https://'); }; // HTML escape helper const escapeHtml = (str) => { if (!str) return ''; return String(str) .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); }; // Normalize measurement values const normalizeMeasure = (val) => { if (val === undefined || val === null || val === '') return '-'; const str = String(val).trim(); if (!str || str === '-') return '-'; const num = parseFloat(str); if (isNaN(num)) return str; if (num === 0) return '-'; // Remove unnecessary decimals: 19.00 -> 19, 19.50 -> 19.5 if (num === Math.floor(num)) { return String(Math.floor(num)); } return String(num); }; // ========== PRODUCT BASE DATA ========== const code = get('code'); const variantCode = get('variant_code'); const variant = get('variant'); // Renk const name = get('name'); const title = get('title') || name; const brand = get('brand'); const season = get('season'); const group = get('group'); const setContent = get('set_content'); // Fiyat const priceNode = item.price ? one(item.price) : null; let priceAmount = '0'; if (priceNode) { if (typeof priceNode === 'object' && priceNode.amount !== undefined) { priceAmount = String(priceNode.amount); } else { priceAmount = String(priceNode); } } const price = utils.formatPrice ? utils.formatPrice(priceAmount) : parseFloat(priceAmount || 0).toFixed(2); // Kategori const categoryNode = item.category ? one(item.category) : null; const mainCat = categoryNode ? txt(one(categoryNode.main)) || '' : ''; const sub1Cat = categoryNode ? txt(one(categoryNode.sub1)) || '' : ''; const sub2Cat = categoryNode ? txt(one(categoryNode.sub2)) || '' : ''; // Attributes const attributesNode = item.attributes ? one(item.attributes) : null; const attributes = []; if (attributesNode) { Object.keys(attributesNode).forEach(key => { const val = txt(one(attributesNode[key])); if (val) attributes.push(`${key}: ${val}`); }); } // Translations const translationsNode = item.translations ? one(item.translations) : null; const englishColor = translationsNode ? txt(one(translationsNode.english)) || variant : variant; // Barcode const barcodeNode = item.barcode ? one(item.barcode) : null; const asortiBarcode = barcodeNode ? txt(one(barcodeNode.asorti)) || '' : ''; // Görseller const imagesNode = item.images ? one(item.images) : null; const imageArr = imagesNode && imagesNode.image ? (Array.isArray(imagesNode.image) ? imagesNode.image : [imagesNode.image]) : []; const productImages = imageArr.map(img => String(txt(img) || '').trim()).filter(validUrl); // ========== SIZE & STOCK PARSING ========== const sizesNode = item.sizes ? one(item.sizes) : null; const namesNode = sizesNode && sizesNode.names ? one(sizesNode.names) : null; const quantitiesNode = sizesNode && sizesNode.quantities ? one(sizesNode.quantities) : null; const sizeNames = namesNode && namesNode.name ? (Array.isArray(namesNode.name) ? namesNode.name : [namesNode.name]) : []; const sizeQuantities = quantitiesNode && quantitiesNode.quantity ? (Array.isArray(quantitiesNode.quantity) ? quantitiesNode.quantity : [quantitiesNode.quantity]) : []; const sizes = sizeNames.map((sizeName, idx) => { const size = String(txt(sizeName) || '').trim(); const qtyValue = sizeQuantities[idx] !== undefined ? txt(sizeQuantities[idx]) : 0; const qty = parseInt(qtyValue) || 0; return { size, qty }; }).filter(s => s.size !== ''); // ========== MEASUREMENTS (Olcu Tablosu) ========== const measurementsNode = item.measurements ? one(item.measurements) : null; const measurementArr = measurementsNode && measurementsNode.measurement ? (Array.isArray(measurementsNode.measurement) ? measurementsNode.measurement : [measurementsNode.measurement]) : []; const generateSizeTable = () => { if (measurementArr.length === 0 || sizes.length === 0) return ''; // Deduplicate measurements by name to avoid duplicate rows const seenNames = new Set(); const uniqueMeasurements = []; for (const m of measurementArr) { const mName = txt(one(m.name)) || ''; if (!seenNames.has(mName) && mName) { seenNames.add(mName); uniqueMeasurements.push(m); } } // Compact styles const s1 = 'border:1px solid #ddd;padding:8px'; const s2 = s1 + ';text-align:center'; const s3 = s1 + ';background:#e8f4f8;font-weight:bold;text-align:center'; let html = '<div style="margin-top:20px">'; html += '<h4>Ölçü Tablosu (cm)</h4>'; html += '<table style="border-collapse:collapse;width:100%">'; // header html += '<tr style="background:#f5f5f5">'; html += `<th style="${s1};text-align:left">Ölçü</th>`; sizes.forEach(sizeObj => { html += `<th style="${s2}">${escapeHtml(sizeObj.size)}</th>`; }); html += '</tr>'; // rows for (const measurement of uniqueMeasurements) { const mName = txt(one(measurement.name)) || ''; const mDesc = txt(one(measurement.description)) || ''; const valuesNode = measurement.values ? one(measurement.values) : null; const valueArr = valuesNode && valuesNode.value ? (Array.isArray(valuesNode.value) ? valuesNode.value : [valuesNode.value]) : []; // EXACTLY like original: if no values array length, it's a section header if (!valueArr.length) { // başlık satırı (örn: PİJAMA vb.) if (mName) { html += `<tr><td colspan="${sizes.length + 1}" style="border:1px solid #ddd;padding:8px;background:#e8f4f8;font-weight:bold;text-align:center;">${escapeHtml(mName)}</td></tr>`; } continue; } html += '<tr>'; let cell = escapeHtml(mName); if (mDesc) cell += `<br><small style="color:#666;">${escapeHtml(mDesc)}</small>`; html += `<td style="border:1px solid #ddd;padding:8px;">${cell}</td>`; sizes.forEach((_, idx) => { const v = valueArr[idx] !== undefined ? txt(valueArr[idx]) : undefined; const displayVal = (v !== undefined && v !== null) ? normalizeMeasure(v) : '-'; html += `<td style="border:1px solid #ddd;padding:8px;text-align:center;">${escapeHtml(displayVal)}</td>`; }); html += '</tr>'; } html += '</table></div>'; return html; }; // ========== ARCHIVE RULES ========== const hasValidPrice = parseFloat(price) > 0; const hasImages = productImages.length > 0; const hasLowStock = sizes.some(s => s.qty < 20); const shouldArchive = !hasValidPrice || !hasImages || hasLowStock; const status = shouldArchive ? 'archived' : 'active'; // ========== BODY HTML ========== let bodyHTML = ''; if (setContent) { bodyHTML += `<p><strong>Set Icerik Bilgisi:</strong> ${setContent}</p>\n`; } if (attributes.length > 0) { bodyHTML += `<p><strong>Ozellikler:</strong> ${attributes.join(', ')}</p>\n`; } if (season) { bodyHTML += `<p><strong>Sezon:</strong> ${season}</p>\n`; } // Olcu tablosunu ekle const sizeTable = generateSizeTable(); if (sizeTable) { bodyHTML += '\n' + sizeTable; } bodyHTML = bodyHTML.trim(); // ========== TEMPLATES ========== const productKey = `${code}-${variantCode}`; const handle = code; // Sadece stokkodu gibi const tags = [brand, mainCat, sub1Cat, sub2Cat, group, season] .filter(Boolean) .join(', '); // İLK SATIR: Full product data const baseFull = { __PRODUCT_KEY: productKey, Handle: handle, Title: utils.normalizeTitle ? utils.normalizeTitle(title) : title, 'Body (HTML)': utils.cleanHTML ? utils.cleanHTML(bodyHTML) : bodyHTML, Vendor: utils.normalizeVendor ? utils.normalizeVendor(brand) : brand, Type: utils.normalizeProductType ? utils.normalizeProductType(sub2Cat || sub1Cat || mainCat) : (sub2Cat || sub1Cat || mainCat), Tags: utils.formatTags ? utils.formatTags(tags) : tags, Published: 'TRUE', Status: status, 'Option1 Name': 'Beden', 'Option2 Name': 'Renk', 'Option3 Name': '', 'Variant Inventory Tracker': 'shopify', 'Variant Inventory Policy': 'deny', 'Variant Fulfillment Service': 'manual', 'Variant Requires Shipping': 'TRUE', 'Variant Taxable': 'TRUE', 'Variant Weight Unit': 'g', 'Variant Grams': 0, 'Gift Card': '', 'SEO Title': utils.normalizeTitle ? utils.normalizeTitle(title) : title, 'SEO Description': '' }; // SONRAKI SATIRLAR: Sadece Handle + varyant const baseLite = { __PRODUCT_KEY: productKey, Handle: handle, Title: '', 'Body (HTML)': '', Vendor: '', Type: '', Tags: '', Published: '', Status: '', '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', 'Variant Weight Unit': 'g', 'Variant Grams': 0, 'Gift Card': '', 'SEO Title': '', 'SEO Description': '' }; // ========== GENERATE ROWS ========== const rows = []; let imagePosition = 1; let isFirstRow = true; if (sizes.length === 0) { // Beden yoksa const row = { ...(isFirstRow ? baseFull : baseLite) }; if (isFirstRow) { row['Option1 Name'] = 'Renk'; row['Option2 Name'] = ''; } row['Option1 Value'] = utils.normalizeOption ? utils.normalizeOption(englishColor) : englishColor; row['Option2 Value'] = ''; row['Option3 Value'] = ''; row['Variant SKU'] = utils.normalizeSKU ? utils.normalizeSKU(asortiBarcode || productKey) : (asortiBarcode || productKey); row['Variant Price'] = price; row['Variant Inventory Qty'] = 0; if (isFirstRow && productImages.length > 0) { row['Image Src'] = productImages[0]; row['Image Position'] = imagePosition++; row['Image Alt Text'] = title; row['Variant Image'] = productImages[0]; } rows.push(row); isFirstRow = false; } else { // Bedenler var sizes.forEach((sizeObj) => { const row = { ...(isFirstRow ? baseFull : baseLite) }; row['Option1 Value'] = utils.normalizeOption ? utils.normalizeOption(sizeObj.size) : sizeObj.size; row['Option2 Value'] = utils.normalizeOption ? utils.normalizeOption(englishColor) : englishColor; row['Option3 Value'] = ''; row['Variant SKU'] = utils.normalizeSKU ? utils.normalizeSKU(`${asortiBarcode || code}-${variantCode}-${sizeObj.size}`) : `${asortiBarcode || code}-${variantCode}-${sizeObj.size}`; row['Variant Price'] = price; row['Variant Inventory Qty'] = sizeObj.qty; // İlk satıra ilk görseli ekle if (isFirstRow && productImages.length > 0) { row['Image Src'] = productImages[0]; row['Image Position'] = imagePosition++; row['Image Alt Text'] = title; row['Variant Image'] = productImages[0]; } rows.push(row); isFirstRow = false; }); } // ========== EXTRA IMAGES ========== if (productImages.length > 1) { for (let i = 1; i < productImages.length; i++) { const src = productImages[i]; if (!validUrl(src)) continue; rows.push({ __PRODUCT_KEY: productKey, Handle: handle, 'Image Src': src, 'Image Position': imagePosition++, 'Image Alt Text': title }); } } // ========== VALIDATION & CLEANUP ========== const safeRows = rows.filter(r => { const hasSku = r['Variant SKU'] && String(r['Variant SKU']).trim() !== ''; const hasImage = r['Image Src'] && String(r['Image Src']).trim() !== ''; if (!hasSku && !hasImage) return false; if (r['Image Src'] && !validUrl(r['Image Src'])) return false; if (!hasImage) { delete r['Image Alt Text']; delete r['Image Position']; } if (!r['Variant Image']) delete r['Variant Image']; return true; }); return safeRows; };
Güncelle
İptal
İstatistikler
Toplam Çalışma
2956
Başarı Oranı
84%
İşlenen Ürün
0
Push Edilen
0