From fa77952ea66919376760675db8b4c2cf9cd5a9cf Mon Sep 17 00:00:00 2001 From: imnavajas Date: Tue, 13 May 2025 13:28:54 +0200 Subject: [PATCH] Corregidos bugs y homegeneizado --- ci4/app/Config/Routes/ImportadoresRoutes.php | 1 + ci4/app/Config/Routes/ScriptsRoutes.php | 2 +- .../Importadores/ImportadorBubok.php | 80 +++++- .../pages/importadores/bubok/bubok_tool.js | 230 +++++++++++++----- 4 files changed, 242 insertions(+), 71 deletions(-) diff --git a/ci4/app/Config/Routes/ImportadoresRoutes.php b/ci4/app/Config/Routes/ImportadoresRoutes.php index 2706aa8c..f3e20ae5 100644 --- a/ci4/app/Config/Routes/ImportadoresRoutes.php +++ b/ci4/app/Config/Routes/ImportadoresRoutes.php @@ -32,6 +32,7 @@ $routes->group('importador', ['namespace' => 'App\Controllers\Importadores'], fu /**====================== * AJAX *========================**/ + $routes->post('validar-fila', 'ImportadorBubok::validarFila'); $routes->post('importar-fila', 'ImportadorBubok::importarFila'); }); diff --git a/ci4/app/Config/Routes/ScriptsRoutes.php b/ci4/app/Config/Routes/ScriptsRoutes.php index f7e994c9..d3744033 100644 --- a/ci4/app/Config/Routes/ScriptsRoutes.php +++ b/ci4/app/Config/Routes/ScriptsRoutes.php @@ -7,7 +7,7 @@ use CodeIgniter\Router\RouteCollection; /* Rutas para tarifas */ $routes->group('scripts', ['namespace' => 'App\Controllers\Scripts'], function ($routes) { - $routes->get('completar-identidades', 'UsersIntegrity::completarIdentidades'); + //$routes->get('completar-identidades', 'UsersIntegrity::completarIdentidades'); }); \ No newline at end of file diff --git a/ci4/app/Controllers/Importadores/ImportadorBubok.php b/ci4/app/Controllers/Importadores/ImportadorBubok.php index 33fcc4d5..27641c64 100644 --- a/ci4/app/Controllers/Importadores/ImportadorBubok.php +++ b/ci4/app/Controllers/Importadores/ImportadorBubok.php @@ -3,6 +3,7 @@ namespace App\Controllers\Importadores; use App\Controllers\BaseResourceController; use App\Controllers\Presupuestos\Presupuestocliente; +use App\Models\Presupuestos\PresupuestoModel; use App\Services\PresupuestoService; class ImportadorBubok extends BaseResourceController @@ -50,6 +51,73 @@ class ImportadorBubok extends BaseResourceController return view(static::$viewPath . 'viewImportadorBubokTool', $viewData); } + public function validarFila() + { + $json = $this->request->getJSON(); + + if (!$json || empty($json->producto) || empty($json->pedido)) { + return $this->response->setJSON([ + 'apto' => false, + 'reason' => 'Datos incompletos' + ]); + } + + $producto = $json->producto; + $pedido = $json->pedido; + + // Validar existencia de ID de producto + if (empty($producto->id)) { + return $this->response->setJSON([ + 'apto' => false, + 'reason' => 'ID de producto no proporcionado' + ]); + } + + $refCliente = $pedido->orderNumber . '-' . $producto->id; + + // Validar formato Ref. Cliente + if (strpos($refCliente, '-') === false || strlen($refCliente) < 5) { + return $this->response->setJSON([ + 'apto' => false, + 'reason' => 'Ref. cliente inválido' + ]); + } + + // 1. Verificar si ya fue importado + $presupuestoModel = new PresupuestoModel(); + $yaExiste = $presupuestoModel->where('referencia_cliente', $refCliente)->first(); + + if ($yaExiste) { + return $this->response->setJSON([ + 'apto' => false, + 'reason' => 'Referencia ya importada' + ]); + } + + // 2. Validación básica del producto (puedes expandir con más reglas si lo necesitas) + $errores = []; + + if (empty($producto->title)) + $errores[] = 'Falta título'; + if (empty($producto->body->pages)) + $errores[] = 'Faltan páginas'; + if (empty($producto->amount)) + $errores[] = 'Falta tirada'; + + if (!empty($errores)) { + return $this->response->setJSON([ + 'apto' => false, + 'reason' => implode(', ', $errores) + ]); + } + + // 3. Producto considerado apto + return $this->response->setJSON([ + 'apto' => true + ]); + } + + public function importarFila() { @@ -83,7 +151,7 @@ class ImportadorBubok extends BaseResourceController 'message' => 'Número de orden o ID del producto no reconocidos.' ]); } - $refCliente = "$orderNumber - $productId"; + $refCliente = "$orderNumber-$productId"; // Titulo $titulo = $producto->title ?? null; @@ -331,12 +399,12 @@ class ImportadorBubok extends BaseResourceController 'ivaReducido' => 1, ]; - return $this->respond([ + /*return $this->respond([ 'status' => 400, 'message' => $dataToImport, 'interiorTipo' => $interiorTipo, 'isColor' => $isColor - ]); + ]);*/ // 5. Guardar try { @@ -368,11 +436,11 @@ class ImportadorBubok extends BaseResourceController ]; } } - + // confirmar y crear pedido y ot $presupuestoModel->confirmarPresupuesto($response['sk_id']); - PresupuestoService::crearPedido($response['sk_id'],isImported:true); - + PresupuestoService::crearPedido($response['sk_id'], isImported: true); + if (!isset($response['sk_id'])) { return $this->respond([ diff --git a/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js b/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js index 76325766..b86902b1 100644 --- a/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js +++ b/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js @@ -1,8 +1,11 @@ document.addEventListener('DOMContentLoaded', function () { + let dataTable; let productosOriginales = []; let datosComunesPedido = {}; + document.getElementById('importBtn').disabled = true; + document.getElementById('xmlFile').addEventListener('change', function (e) { const file = e.target.files[0]; if (!file || !file.name.endsWith('.zip')) { @@ -10,10 +13,19 @@ document.addEventListener('DOMContentLoaded', function () { return; } + document.getElementById('importBtn').disabled = true; + const reader = new FileReader(); reader.onload = async function (event) { try { const zip = await JSZip.loadAsync(event.target.result); + + // Reset tabla y memoria de productos + if (dataTable) { + dataTable.clear().draw(); + } + productosOriginales = []; + const xmlFiles = Object.values(zip.files).filter(f => f.name.endsWith('.xml') && !f.name.startsWith('__MACOSX/') ); @@ -23,11 +35,20 @@ document.addEventListener('DOMContentLoaded', function () { return; } + Swal.fire({ + title: 'Procesando...', + text: 'Cargando XMLs...', + allowOutsideClick: false, + showDenyButton: false, + didOpen: () => Swal.showLoading() + }); + for (const file of xmlFiles) { const content = await file.async('text'); - parseXmlAndLoadTable(content); + await parseXmlAndLoadTable(content); } + Swal.close(); Swal.fire('Éxito', `${xmlFiles.length} archivos XML cargados.`, 'success'); } catch (err) { console.error(err); @@ -38,8 +59,8 @@ document.addEventListener('DOMContentLoaded', function () { reader.readAsArrayBuffer(file); }); - function parseXmlAndLoadTable(xmlText) { - let parser = new DOMParser(); + async function parseXmlAndLoadTable(xmlText) { + const parser = new DOMParser(); let xmlDoc; try { @@ -50,7 +71,7 @@ document.addEventListener('DOMContentLoaded', function () { return; } - datosComunesPedido = { + const pedidoActual = { orderNumber: xmlDoc.querySelector('orderNumber')?.textContent ?? '', shipping: { address: xmlDoc.querySelector('shippingData > address')?.textContent ?? '', @@ -65,13 +86,19 @@ document.addEventListener('DOMContentLoaded', function () { const products = Array.from(xmlDoc.getElementsByTagName('product')); const offset = productosOriginales.length; + const rows = []; - products.forEach((prod, idx) => { - productosOriginales.push({ index: offset + idx, data: prod }); - }); + for (let i = 0; i < products.length; i++) { + const product = products[i]; + const globalIndex = offset + i; + + // Asociar producto con su pedido + productosOriginales.push({ + index: globalIndex, + data: product, + pedido: { ...pedidoActual } + }); - const rows = products.map((product, index) => { - const globalIndex = offset + index; const id = product.querySelector('id')?.textContent ?? ''; const title = product.querySelector('title')?.textContent ?? ''; const pages = product.querySelector('body > pages')?.textContent ?? ''; @@ -97,40 +124,65 @@ document.addEventListener('DOMContentLoaded', function () { } }); - return [ - ``, - `${datosComunesPedido.orderNumber} - ${id}`, - title, - tamano, - pages, - tirada, - interior, - '', - ` - ` - ]; - }); + // Obtener referencia del pedido + const refCliente = `${pedidoActual.orderNumber}-${id}`; + + // Validar fila con su propio pedido + const result = await validarProductoXml(product, pedidoActual); + + let checkboxHtml = ''; + let notaHtml = ''; + let actionBtnsHtml = ''; + + if (result.apto) { + checkboxHtml = ``; + notaHtml = ''; + actionBtnsHtml = ` +
+ + +
+ `; + } else { + checkboxHtml = ``; + notaHtml = `${result.reason}`; + actionBtnsHtml = ` +
+ No Apto + +
+ `; + } + + rows.push([checkboxHtml, refCliente, title, tamano, pages, tirada, interior, notaHtml, actionBtnsHtml]); + } if (!$.fn.DataTable.isDataTable('#xmlTable')) { - const headerHtml = ` - - - - Referencia - Título - Tamaño - Páginas - Tirada - Interior - Notas - Acciones - - - - - - `; - $('#xmlTable').html(headerHtml); + $('#xmlTable').html(` + + + + Referencia + Título + Tamaño + Páginas + Tirada + Interior + Notas + Acciones + + + + + + + `); } if (!dataTable) { @@ -141,6 +193,8 @@ document.addEventListener('DOMContentLoaded', function () { responsive: true, scrollX: true, dom: 'lfrtip', + pageLength: 25, + lengthMenu: [5, 10, 25, 50, 100, 250, 500], language: { url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json" }, @@ -163,8 +217,36 @@ document.addEventListener('DOMContentLoaded', function () { rows.forEach(row => dataTable.row.add(row)); dataTable.draw(false); } + + if (rows.length > 0) { + document.getElementById('importBtn').disabled = false; + } } + + async function validarProductoXml(productoXml, pedido) { + try { + const response = await fetch('/importador/bubok/validar-fila', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-TOKEN': '' + }, + body: JSON.stringify({ + producto: xmlToJson(productoXml), + pedido: pedido + }) + }); + + return await response.json(); + } catch (error) { + console.error('Error validando producto:', error); + return { apto: false, reason: 'Error conexión' }; + } + } + + + function setupEventListeners() { $('#xmlTable tbody').off('click', '.deleteRow').on('click', '.deleteRow', function () { dataTable.row($(this).parents('tr')).remove().draw(); @@ -173,9 +255,12 @@ document.addEventListener('DOMContentLoaded', function () { $('#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; + const productoObj = productosOriginales.find(p => p.index === rowIndex); - if (!xmlProduct) return; + if (!productoObj) return; + + const xmlProduct = productoObj.data; + const pedido = productoObj.pedido; try { const response = await fetch('/importador/bubok/importar-fila', { @@ -186,32 +271,46 @@ document.addEventListener('DOMContentLoaded', function () { }, body: JSON.stringify({ producto: xmlToJson(xmlProduct), - pedido: datosComunesPedido + pedido: pedido }) }); 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 - ? `Ver presupuesto` - : 'Importado'; - rowData[7] = htmlLink; + const skId = result.data?.sk_id ?? ''; + + rowData[7] = skUrl + ? `Ver presupuesto (${skId})` + : `Importado (${skId})`; + dataTable.row($row).data(rowData).draw(false); + let html = skUrl + ? `La fila se importó exitosamente.

Ver presupuesto (${skId})` + : `La fila se importó exitosamente. (${skId})`; + + let icon = 'success'; + + if (result.price_warning) { + html = skUrl + ? `La fila se importó con éxito, pero el precio fue ajustado por debajo de costes.

Ver presupuesto (${skId})` + : `La fila se importó con advertencia de coste. (${skId})`; + icon = 'warning'; + } + Swal.fire({ title: 'Importado correctamente', - html: skUrl - ? `Se importó correctamente.
Ver presupuesto` - : 'Importación realizada.', - icon: 'success', + html: html, + icon: icon, confirmButtonText: 'Aceptar', customClass: { confirmButton: 'btn btn-success' } }); + } else { - rowData[7] = `${result.message ?? 'Error inesperado'}`; + rowData[7] = `${result.message ?? 'Error desconocido'}`; dataTable.row($row).data(rowData).draw(false); Swal.fire({ @@ -222,15 +321,14 @@ document.addEventListener('DOMContentLoaded', function () { customClass: { confirmButton: 'btn btn-danger' } }); } - - dataTable.row($row).data(rowData).draw(false); - - } catch (err) { - console.error(err); + } catch (error) { + console.error('Importación fallida:', error); 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); @@ -329,8 +427,11 @@ document.addEventListener('DOMContentLoaded', function () { if (!result.isConfirmed) return; for (const i of filasSeleccionadas) { - const productXml = productosOriginales.find(p => p.index === i)?.data; - if (!productXml) continue; + const productoObj = productosOriginales.find(p => p.index === i); + if (!productoObj) continue; + + const productXml = productoObj.data; + const pedido = productoObj.pedido; try { const response = await fetch('/importador/bubok/importar-fila', { @@ -341,7 +442,7 @@ document.addEventListener('DOMContentLoaded', function () { }, body: JSON.stringify({ producto: xmlToJson(productXml), - pedido: datosComunesPedido + pedido: pedido }) }); @@ -351,9 +452,10 @@ document.addEventListener('DOMContentLoaded', function () { if (response.ok && result.status === 200) { const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null; + const skId = result.data?.sk_id ?? ''; rowData[7] = skUrl - ? `Ver presupuesto` - : 'Importado'; + ? `Ver presupuesto (${skId})` + : `Importado (${skId})`; } else { rowData[7] = `${result.message ?? 'Error desconocido'}`; }