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 c8955350..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; @@ -277,6 +345,15 @@ class ImportadorBubok extends BaseResourceController ] ]; + // Recalcular calidad (isColor y isHq) en funcion del cliente + [$isColor, $isHq] = PresupuestoService::getCalidad( + 'importador-bubok', + null, + ((trim(strtolower($interiorTipo)) === 'color') ? 1 : 0), + 0, + intval($tirada ?? 0) + ); + // Generamos el objeto a importar $dataToImport = [ 'selectedTirada' => $tirada, @@ -295,8 +372,8 @@ class ImportadorBubok extends BaseResourceController 'tipo' => '', 'tipo_presupuesto_id' => $encuadernadoId, 'clienteId' => 40, // BUBOK ID - 'isColor' => ($interiorTipo === 'color') ? 1 : 0, - 'isHq' => 0, + 'isColor' => $isColor, + 'isHq' => $isHq, 'paginas' => $paginas, 'paginasColor' => ($interiorTipo === 'color') ? $paginas : 0, 'paginasCuadernillo' => 32, @@ -317,15 +394,16 @@ class ImportadorBubok extends BaseResourceController 'sobrecubierta' => [], 'faja' => null, - 'entrega_taller' => 1, - //'direcciones' => $direcciones, las direcciones que aparecen no se añaden, ya que la recogida la hacen ellos con su empresa de mensajeria + 'direcciones' => $direcciones, 'ivaReducido' => 1, ]; /*return $this->respond([ 'status' => 400, - 'message' => $dataToImport + 'message' => $dataToImport, + 'interiorTipo' => $interiorTipo, + 'isColor' => $isColor ]);*/ // 5. Guardar @@ -358,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/ci4/app/Language/es/Importador.php b/ci4/app/Language/es/Importador.php index 925076fc..5f4f0ad0 100644 --- a/ci4/app/Language/es/Importador.php +++ b/ci4/app/Language/es/Importador.php @@ -12,7 +12,7 @@ return [ 'precio_compra' => 'Precio Compra', 'importar' => 'Importar', 'subirArchivoRama' => 'Cargar Excel proporcionado por RA-MA', - 'subirArchivoBubok' => 'Cargar XML proporcionado por BUBOK', + 'subirArchivoBubok' => 'Cargar ZIP proporcionado por BUBOK', 'libro' => 'libro', 'id' => 'ID', diff --git a/ci4/app/Views/themes/vuexy/form/importador/bubok/viewImportadorBubokTool.php b/ci4/app/Views/themes/vuexy/form/importador/bubok/viewImportadorBubokTool.php index 4db87bfe..1e89f976 100644 --- a/ci4/app/Views/themes/vuexy/form/importador/bubok/viewImportadorBubokTool.php +++ b/ci4/app/Views/themes/vuexy/form/importador/bubok/viewImportadorBubokTool.php @@ -22,8 +22,8 @@
- + class="form-label"> +
- ` - ]; - }); + // Obtener referencia del pedido + const refCliente = `${pedidoActual.orderNumber}-${id}`; - if (!$.fn.DataTable.isDataTable('#xmlTable')) { - const headerHtml = ` - - - - Referencia - Título - Tamaño - Páginas - Tirada - Interior - Notas - Acciones - - - - - - `; - $('#xmlTable').html(headerHtml); + // 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]); } - 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']] - }); + if (!$.fn.DataTable.isDataTable('#xmlTable')) { + $('#xmlTable').html(` + + + + Referencia + Título + Tamaño + Páginas + Tirada + Interior + Notas + Acciones + + + + + + + `); + } - $('#xmlTable thead tr:eq(1) th').each(function (i) { - if (![0, 7, 8].includes(i)) { - $(this).html(''); - $('input', this).on('keyup change', function () { - if (dataTable.column(i).search() !== this.value) { - dataTable.column(i).search(this.value).draw(); - } - }); - } - }); + if (!dataTable) { + dataTable = $('#xmlTable').DataTable({ + destroy: true, + data: rows, + orderCellsTop: true, + 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" + }, + order: [[1, 'asc']] + }); - setupEventListeners(); + $('#xmlTable thead tr:eq(1) th').each(function (i) { + if (![0, 7, 8].includes(i)) { + $(this).html(''); + $('input', this).on('keyup change', function () { + if (dataTable.column(i).search() !== this.value) { + dataTable.column(i).search(this.value).draw(); + } + }); + } + }); + + setupEventListeners(); + } else { + 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(); @@ -140,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', { @@ -153,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({ @@ -189,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); @@ -258,9 +389,9 @@ document.addEventListener('DOMContentLoaded', function () { }); return; } - + const filasSeleccionadas = []; - + dataTable.rows().every(function () { const node = this.node(); const checkbox = $(node).find('input.select-row'); @@ -268,7 +399,7 @@ document.addEventListener('DOMContentLoaded', function () { filasSeleccionadas.push(this.index()); } }); - + if (filasSeleccionadas.length === 0) { Swal.fire({ title: 'Atención', @@ -279,7 +410,7 @@ document.addEventListener('DOMContentLoaded', function () { }); return; } - + Swal.fire({ title: '¿Importar seleccionados?', text: `Se van a importar ${filasSeleccionadas.length} filas.`, @@ -294,11 +425,14 @@ document.addEventListener('DOMContentLoaded', function () { } }).then(async (result) => { 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', { method: 'POST', @@ -308,29 +442,30 @@ document.addEventListener('DOMContentLoaded', function () { }, body: JSON.stringify({ producto: xmlToJson(productXml), - pedido: datosComunesPedido + pedido: pedido }) }); - + 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; + const skId = result.data?.sk_id ?? ''; rowData[7] = skUrl - ? `Ver presupuesto` - : 'Importado'; + ? `Ver presupuesto (${skId})` + : `Importado (${skId})`; } else { rowData[7] = `${result.message ?? 'Error desconocido'}`; } - + 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.', @@ -340,5 +475,5 @@ document.addEventListener('DOMContentLoaded', function () { }); }); }); - + });