// ─── MercadoLibre Bulk Publish Modal ─────────────────────────────────── var meliPreviewData = {}; var meliCategoryAttrs = []; var meliCategorySearchTimeout; var meliCatItems = []; var meliCatActiveIndex = -1; window.openMeliPublishModal = function() { if (selectedItems.size === 0) { showToast('Selecciona al menos un producto', 'warn'); return; } document.getElementById('meliPublishModal').classList.add('is-open'); document.getElementById('meliPublishResult').innerHTML = ''; document.getElementById('meliCategoryId').value = ''; document.getElementById('meliCategorySearch').value = ''; document.getElementById('meliCategoryResults').innerHTML = ''; document.getElementById('meliAttrsSection').style.display = 'none'; document.getElementById('meliAttrsGrid').innerHTML = ''; meliCategoryAttrs = []; refreshMeliPublishPreview(); }; window.closeMeliPublishModal = function() { document.getElementById('meliPublishModal').classList.remove('is-open'); }; // ==== RESTAURADAS v4 (MELI + descuentos) ==== window.searchMeliCategories = function() { var q = document.getElementById('meliCategorySearch').value.trim(); var resultsDiv = document.getElementById('meliCategoryResults'); if (q.length < 2) { resultsDiv.innerHTML = ''; meliCatItems = []; meliCatActiveIndex = -1; return; } clearTimeout(meliCategorySearchTimeout); resultsDiv.innerHTML = '
Buscando...
'; meliCategorySearchTimeout = setTimeout(function() { fetch('/pos/api/marketplace-ext/categories?q=' + encodeURIComponent(q), { headers: { 'Authorization': 'Bearer ' + getToken(), 'Content-Type': 'application/json' } }) .then(function(r) { return r.json(); }) .then(function(data) { var cats = data.categories || []; meliCatItems = cats.slice(0, 10); meliCatActiveIndex = -1; if (!meliCatItems.length) { resultsDiv.innerHTML = '
Sin resultados
'; return; } var html = '
'; meliCatItems.forEach(function(c, idx) { html += '
' + '' + esc(c.category_name || c.category_id) + '' + '' + esc(c.category_id) + '' + '
'; }); html += '
'; resultsDiv.innerHTML = html; }) .catch(function() { resultsDiv.innerHTML = ''; meliCatItems = []; meliCatActiveIndex = -1; }); }, 300); }; window.handleMeliCatKeydown = function(e) { if (!meliCatItems.length) return; if (e.key === 'ArrowDown') { e.preventDefault(); meliCatActiveIndex = Math.min(meliCatActiveIndex + 1, meliCatItems.length - 1); highlightMeliCat(meliCatActiveIndex); } else if (e.key === 'ArrowUp') { e.preventDefault(); meliCatActiveIndex = Math.max(meliCatActiveIndex - 1, -1); highlightMeliCat(meliCatActiveIndex); } else if (e.key === 'Enter') { e.preventDefault(); if (meliCatActiveIndex >= 0) selectMeliCategoryIdx(meliCatActiveIndex); } else if (e.key === 'Escape') { document.getElementById('meliCategoryResults').innerHTML = ''; meliCatItems = []; meliCatActiveIndex = -1; } }; /* Cerrar dropdown al hacer click fuera */ document.addEventListener('click', function(e) { var field = document.getElementById('meliCategorySearch'); var results = document.getElementById('meliCategoryResults'); if (field && results && !field.contains(e.target) && !results.contains(e.target)) { results.innerHTML = ''; meliCatItems = []; meliCatActiveIndex = -1; } }); window.onMeliShippingChange = function() { var mode = document.getElementById('meliShippingMode').value; var costField = document.getElementById('meliShippingCostField'); if (costField) costField.style.display = (mode === 'custom') ? 'block' : 'none'; }; // ─── MELI publish helpers ────────────────────────────────────────────── // Contrato de `custom_data` esperado por validate_items / publish_items // (pos/services/marketplace_external_service.py): // { // titles: { "": "Título (<=60)" }, // prices: { "": 123.45 }, // stocks: { "": 3 }, // attributes: { "": [{ id: "BRAND", value_name: "Nissan" }] }, // shipping_cost: 150 // solo se usa si shipping_mode === 'custom' // } // Las claves son el id de inventario como string (el backend consulta con str(inv_id)). function _meliAttrValue(attr) { // Los atributos con lista cerrada se pintan como ' + ''; } html += '
' + imgHtml + '
' + '' + '
' + it.title.length + '/60
' + '
' + checks + '
' + '
' + '
' + '
' + '
' + '
'; }); container.innerHTML = html; scheduleMeliFeeEstimates(); }).catch(function() { container.innerHTML = '

Error de red

'; }); } window.selectMeliCategory = function(id, name) { document.getElementById('meliCategoryId').value = id; document.getElementById('meliCategorySearch').value = name; document.getElementById('meliCategoryResults').innerHTML = ''; meliCatItems = []; meliCatActiveIndex = -1; loadCategoryAttributes(id); scheduleMeliFeeEstimates(); }; window.selectMeliCategoryIdx = function(idx) { var c = meliCatItems[idx]; if (!c) return; selectMeliCategory(c.category_id, c.category_name || c.category_id); }; window.highlightMeliCat = function(idx) { meliCatActiveIndex = idx; var items = document.querySelectorAll('.meli-cat-item'); items.forEach(function(el, i) { el.classList.toggle('is-active', i === idx); }); }; window.loadCategoryAttributes = function(categoryId) { var grid = document.getElementById('meliAttrsGrid'); var section = document.getElementById('meliAttrsSection'); grid.innerHTML = '

Cargando atributos...

'; section.style.display = 'block'; fetch('/pos/api/marketplace-ext/categories/' + encodeURIComponent(categoryId) + '/attributes', { headers: { 'Authorization': 'Bearer ' + getToken() } }) .then(function(r){ return r.json(); }) .then(function(data) { meliCategoryAttrs = data.attributes || []; if (!meliCategoryAttrs.length) { grid.innerHTML = '

No hay atributos obligatorios adicionales.

'; return; } var html = ''; meliCategoryAttrs.forEach(function(attr) { var attrId = esc(attr.id); var attrName = esc(attr.name || attr.id); var inputHtml = ''; if (attr.values && attr.values.length) { // Some ML attributes (like BRAND) have a closed list but the API still // accepts free-text via value_name. Provide a select + "Other" fallback. var selectId = 'meliAttrSel-' + attrId; var otherId = 'meliAttrOther-' + attrId; inputHtml = '' + ''; } html += '
' + inputHtml + '
'; }); grid.innerHTML = html; }).catch(function() { grid.innerHTML = '

Error cargando atributos

'; }); }; window.updateMeliTitleCount = function(id) { var el = document.getElementById('meliTitle-' + id); var countEl = document.getElementById('meliTitleCount-' + id); if (el && countEl) countEl.textContent = el.value.length + '/60'; }; window.handleMeliImageUpload = function(itemId, input) { if (!input.files || !input.files[0]) return; var file = input.files[0]; var formData = new FormData(); formData.append('file', file); fetch('/pos/api/inventory/items/' + itemId + '/image', { method: 'POST', headers: { 'Authorization': 'Bearer ' + getToken() }, body: formData }).then(function(r){ return r.json(); }) .then(function(data) { if (data.image_url) { showToast('Imagen subida', 'success'); refreshMeliPublishPreview(); if (inventoryVS) inventoryVS.refresh(); } else { showToast(data.error || 'Error subiendo imagen', 'error'); } }).catch(function(){ showToast('Error de red', 'error'); }); }; // ─── Estimador de comisiones MeLi (por artículo) ─── var meliFeeTimeout; function fmtMoney(n) { return '$' + (parseFloat(n) || 0).toLocaleString('es-MX', {minimumFractionDigits: 2, maximumFractionDigits: 2}); } function updateMeliFeeEstimates() { var categoryId = (document.getElementById('meliCategoryId') || {}).value; var listingType = (document.getElementById('meliListingType') || {}).value || 'gold_special'; var ids = Object.keys(window.meliPreviewData || (typeof meliPreviewData !== 'undefined' ? meliPreviewData : {})); if (!ids.length) return; ids.forEach(function (id) { var feeEl = document.getElementById('meliFee-' + id); if (!feeEl) return; if (!categoryId) { feeEl.innerHTML = ''; return; } var priceEl = document.getElementById('meliPrice-' + id); var price = parseFloat(priceEl && priceEl.value) || 0; if (price <= 0) { feeEl.innerHTML = 'Ingresa un precio para estimar comisiones'; return; } feeEl.innerHTML = 'Calculando comisiones...'; fetch('/pos/api/marketplace-ext/listing-prices?price=' + encodeURIComponent(price) + '&category_id=' + encodeURIComponent(categoryId), { headers: { 'Authorization': 'Bearer ' + getToken() } }) .then(function (r) { return r.json().then(function (d) { return {ok: r.ok, data: d}; }); }) .then(function (res) { if (!res.ok || res.data.error) { feeEl.innerHTML = 'No se pudo estimar: ' + esc(res.data.error || 'error') + ''; return; } var offers = res.data.listing_prices || []; var offer = null; offers.forEach(function (o) { if (o.listing_type_id === listingType) offer = o; }); if (!offer && offers.length) offer = offers[0]; if (!offer || !offer.sale_fee_details) { feeEl.innerHTML = ''; return; } var fee = offer.sale_fee_details.gross_amount || 0; var pct = offer.sale_fee_details.percentage_fee || 0; var net = price - fee; feeEl.innerHTML = '💰 Comisión MeLi: ' + fmtMoney(fee) + ' (' + pct + '%)' + ' · Recibes: ' + fmtMoney(net) + '' + ' (estimado, sin costo de envío)'; }) .catch(function () { feeEl.innerHTML = ''; }); }); } function scheduleMeliFeeEstimates() { clearTimeout(meliFeeTimeout); meliFeeTimeout = setTimeout(updateMeliFeeEstimates, 500); } window.scheduleMeliFeeEstimates = scheduleMeliFeeEstimates; // ===================================================================== // EXPOSE GLOBALS (for onclick handlers in HTML)