mirror of
https://git.imnavajas.es/jjimenez/safekat.git
synced 2025-07-25 22:52:08 +00:00
Primera version del importador de Bubok
This commit is contained in:
@ -0,0 +1,344 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
let dataTable;
|
||||
let productosOriginales = [];
|
||||
let datosComunesPedido = {};
|
||||
|
||||
document.getElementById('xmlFile').addEventListener('change', function (e) {
|
||||
const file = e.target.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (event) {
|
||||
const xmlText = event.target.result;
|
||||
parseXmlAndLoadTable(xmlText);
|
||||
};
|
||||
reader.readAsText(file);
|
||||
});
|
||||
|
||||
function parseXmlAndLoadTable(xmlText) {
|
||||
let parser = new DOMParser();
|
||||
let xmlDoc;
|
||||
|
||||
try {
|
||||
xmlDoc = parser.parseFromString(xmlText, 'application/xml');
|
||||
if (xmlDoc.getElementsByTagName('parsererror').length > 0) throw new Error('XML inválido');
|
||||
} catch (e) {
|
||||
Swal.fire('Error', 'No se pudo leer el XML.', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
datosComunesPedido = {
|
||||
orderNumber: xmlDoc.querySelector('orderNumber')?.textContent ?? '',
|
||||
shipping: {
|
||||
address: xmlDoc.querySelector('shippingData > address')?.textContent ?? '',
|
||||
city: xmlDoc.querySelector('shippingData > city')?.textContent ?? '',
|
||||
country: xmlDoc.querySelector('shippingData > country')?.textContent ?? '',
|
||||
postalCode: xmlDoc.querySelector('shippingData > postalCode')?.textContent ?? '',
|
||||
name: xmlDoc.querySelector('shippingData > name')?.textContent ?? '',
|
||||
phone: xmlDoc.querySelector('shippingData > phone')?.textContent ?? ''
|
||||
},
|
||||
labelUrl: xmlDoc.querySelector('urlPdfSeur')?.textContent ?? ''
|
||||
};
|
||||
|
||||
const products = Array.from(xmlDoc.getElementsByTagName('product'));
|
||||
productosOriginales = products.map((prod, idx) => ({ index: idx, data: prod }));
|
||||
|
||||
const rows = products.map((product, index) => {
|
||||
const id = product.querySelector('id')?.textContent ?? '';
|
||||
const title = product.querySelector('title')?.textContent ?? '';
|
||||
const pages = product.querySelector('body > pages')?.textContent ?? '';
|
||||
const tirada = product.querySelector('amount')?.textContent ?? '';
|
||||
|
||||
let interior = 'Desconocido';
|
||||
const colorNode = product.querySelector('body > color');
|
||||
if (colorNode) {
|
||||
const monochrome = colorNode.querySelector('Monochrome')?.textContent ?? '0';
|
||||
const cmyk = colorNode.querySelector('CMYK')?.textContent ?? '0';
|
||||
const semicolor = colorNode.querySelector('Semicolor')?.textContent ?? '0';
|
||||
|
||||
if (monochrome === '1') interior = 'Negro';
|
||||
else if (cmyk === '1') interior = 'Color';
|
||||
else if (semicolor === '1') interior = 'Semicolor';
|
||||
}
|
||||
|
||||
let tamano = 'Desconocido';
|
||||
const sizeTags = product.querySelectorAll('size > *');
|
||||
sizeTags.forEach(tag => {
|
||||
if (tag.textContent === '1') {
|
||||
tamano = tag.tagName.replace(/^size/, '');
|
||||
}
|
||||
});
|
||||
|
||||
return [
|
||||
`<input type="checkbox" class="form-check-input select-row" checked>`,
|
||||
`${datosComunesPedido.orderNumber} - ${id}`,
|
||||
title,
|
||||
tamano,
|
||||
pages,
|
||||
tirada,
|
||||
interior,
|
||||
'',
|
||||
`<button type="button" class="btn btn-sm btn-outline-success importRow">Importar</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger deleteRow">Eliminar</button>`
|
||||
];
|
||||
});
|
||||
|
||||
if (!$.fn.DataTable.isDataTable('#xmlTable')) {
|
||||
const headerHtml = `
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll" /></th>
|
||||
<th>Referencia</th>
|
||||
<th>Título</th>
|
||||
<th>Tamaño</th>
|
||||
<th>Páginas</th>
|
||||
<th>Tirada</th>
|
||||
<th>Interior</th>
|
||||
<th>Notas</th>
|
||||
<th>Acciones</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th><th></th><th></th><th></th><th></th>
|
||||
<th></th><th></th><th></th><th></th>
|
||||
</tr>
|
||||
</thead>`;
|
||||
$('#xmlTable').html(headerHtml);
|
||||
}
|
||||
|
||||
dataTable = $('#xmlTable').DataTable({
|
||||
destroy: true,
|
||||
data: rows,
|
||||
orderCellsTop: true,
|
||||
responsive: true,
|
||||
scrollX: true,
|
||||
dom: 'lfrtip',
|
||||
language: {
|
||||
url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json"
|
||||
},
|
||||
order: [[1, 'asc']]
|
||||
});
|
||||
|
||||
$('#xmlTable thead tr:eq(1) th').each(function (i) {
|
||||
if (![0, 7, 8].includes(i)) {
|
||||
$(this).html('<input type="text" class="form-control form-control-sm" placeholder="Filtrar..." />');
|
||||
$('input', this).on('keyup change', function () {
|
||||
if (dataTable.column(i).search() !== this.value) {
|
||||
dataTable.column(i).search(this.value).draw();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setupEventListeners();
|
||||
}
|
||||
|
||||
function setupEventListeners() {
|
||||
$('#xmlTable tbody').off('click', '.deleteRow').on('click', '.deleteRow', function () {
|
||||
dataTable.row($(this).parents('tr')).remove().draw();
|
||||
});
|
||||
|
||||
$('#xmlTable tbody').off('click', '.importRow').on('click', '.importRow', async function () {
|
||||
const $row = $(this).closest('tr');
|
||||
const rowIndex = dataTable.row($row).index();
|
||||
const xmlProduct = productosOriginales.find(p => p.index === rowIndex)?.data;
|
||||
|
||||
if (!xmlProduct) return;
|
||||
|
||||
try {
|
||||
const response = await fetch('/importador/bubok/importar-fila', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '<?= csrf_hash() ?>'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
producto: xmlToJson(xmlProduct),
|
||||
pedido: datosComunesPedido
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
const rowData = dataTable.row($row).data();
|
||||
if (response.ok && result.status === 200) {
|
||||
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
||||
const htmlLink = skUrl
|
||||
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto</a>`
|
||||
: 'Importado';
|
||||
rowData[7] = htmlLink;
|
||||
dataTable.row($row).data(rowData).draw(false);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Importado correctamente',
|
||||
html: skUrl
|
||||
? `Se importó correctamente.<br><a href="${skUrl}" target="_blank" class="btn btn-primary mt-2">Ver presupuesto</a>`
|
||||
: 'Importación realizada.',
|
||||
icon: 'success',
|
||||
confirmButtonText: 'Aceptar',
|
||||
customClass: { confirmButton: 'btn btn-success' }
|
||||
});
|
||||
} else {
|
||||
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error inesperado'}</span>`;
|
||||
dataTable.row($row).data(rowData).draw(false);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Error',
|
||||
text: result.message ?? 'Hubo un error al importar esta fila.',
|
||||
icon: 'error',
|
||||
confirmButtonText: 'Aceptar',
|
||||
customClass: { confirmButton: 'btn btn-danger' }
|
||||
});
|
||||
}
|
||||
|
||||
dataTable.row($row).data(rowData).draw(false);
|
||||
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
Swal.fire('Error', 'Error de comunicación con el servidor.', 'error');
|
||||
}
|
||||
});
|
||||
|
||||
$('#selectAll').off('change').on('change', function () {
|
||||
const checked = $(this).is(':checked');
|
||||
$('#xmlTable tbody input.select-row:enabled').prop('checked', checked);
|
||||
});
|
||||
}
|
||||
|
||||
function xmlToJson(xmlNode) {
|
||||
// Si es nodo de texto
|
||||
if (xmlNode.nodeType === 3) {
|
||||
return xmlNode.nodeValue.trim();
|
||||
}
|
||||
|
||||
let obj = {};
|
||||
|
||||
// Procesar atributos si existen
|
||||
if (xmlNode.attributes && xmlNode.attributes.length > 0) {
|
||||
for (let attr of xmlNode.attributes) {
|
||||
obj[`@${attr.name}`] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
// Procesar hijos
|
||||
if (xmlNode.hasChildNodes()) {
|
||||
const children = Array.from(xmlNode.childNodes).filter(n => n.nodeType !== 8); // ignorar comentarios
|
||||
|
||||
// Si el único hijo es texto, devolver como string
|
||||
if (children.length === 1 && children[0].nodeType === 3) {
|
||||
return children[0].nodeValue.trim();
|
||||
}
|
||||
|
||||
// Procesar nodos hijos normalmente
|
||||
children.forEach(child => {
|
||||
const name = child.nodeName;
|
||||
const value = xmlToJson(child);
|
||||
|
||||
if (obj[name]) {
|
||||
if (!Array.isArray(obj[name])) {
|
||||
obj[name] = [obj[name]];
|
||||
}
|
||||
obj[name].push(value);
|
||||
} else {
|
||||
obj[name] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
document.getElementById('importBtn').addEventListener('click', async function () {
|
||||
if (!dataTable || dataTable.rows().count() === 0) {
|
||||
Swal.fire({
|
||||
title: 'Atención',
|
||||
text: 'Primero debes cargar un archivo XML válido.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'Aceptar',
|
||||
customClass: { confirmButton: 'btn btn-warning' }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const filasSeleccionadas = [];
|
||||
|
||||
dataTable.rows().every(function () {
|
||||
const node = this.node();
|
||||
const checkbox = $(node).find('input.select-row');
|
||||
if (checkbox.length > 0 && checkbox.is(':checked') && !checkbox.is(':disabled')) {
|
||||
filasSeleccionadas.push(this.index());
|
||||
}
|
||||
});
|
||||
|
||||
if (filasSeleccionadas.length === 0) {
|
||||
Swal.fire({
|
||||
title: 'Atención',
|
||||
text: 'No hay filas seleccionadas.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'Aceptar',
|
||||
customClass: { confirmButton: 'btn btn-warning' }
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: '¿Importar seleccionados?',
|
||||
text: `Se van a importar ${filasSeleccionadas.length} filas.`,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sí, importar',
|
||||
cancelButtonText: 'Cancelar',
|
||||
reverseButtons: true,
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-primary',
|
||||
cancelButton: 'btn btn-secondary'
|
||||
}
|
||||
}).then(async (result) => {
|
||||
if (!result.isConfirmed) return;
|
||||
|
||||
for (const i of filasSeleccionadas) {
|
||||
const productXml = productosOriginales.find(p => p.index === i)?.data;
|
||||
if (!productXml) continue;
|
||||
|
||||
try {
|
||||
const response = await fetch('/importador/bubok/importar-fila', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-TOKEN': '<?= csrf_hash() ?>'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
producto: xmlToJson(productXml),
|
||||
pedido: datosComunesPedido
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
const rowNode = dataTable.row(i).node();
|
||||
const rowData = dataTable.row(i).data();
|
||||
|
||||
if (response.ok && result.status === 200) {
|
||||
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
||||
rowData[7] = skUrl
|
||||
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto</a>`
|
||||
: '<span class="badge bg-success">Importado</span>';
|
||||
} else {
|
||||
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error desconocido'}</span>`;
|
||||
}
|
||||
|
||||
dataTable.row(rowNode).data(rowData).draw(false);
|
||||
} catch (error) {
|
||||
console.error('Importación fallida:', error);
|
||||
}
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Importación finalizada',
|
||||
text: 'Se han procesado todas las filas seleccionadas.',
|
||||
icon: 'success',
|
||||
confirmButtonText: 'Aceptar',
|
||||
customClass: { confirmButton: 'btn btn-success' }
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user