mirror of
https://git.imnavajas.es/jjimenez/safekat.git
synced 2025-07-25 22:52:08 +00:00
Merge branch 'fix/importador_bubok' into 'main'
Fix/importador bubok See merge request jjimenez/safekat!809
This commit is contained in:
@ -32,6 +32,7 @@ $routes->group('importador', ['namespace' => 'App\Controllers\Importadores'], fu
|
|||||||
/**======================
|
/**======================
|
||||||
* AJAX
|
* AJAX
|
||||||
*========================**/
|
*========================**/
|
||||||
|
$routes->post('validar-fila', 'ImportadorBubok::validarFila');
|
||||||
$routes->post('importar-fila', 'ImportadorBubok::importarFila');
|
$routes->post('importar-fila', 'ImportadorBubok::importarFila');
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use CodeIgniter\Router\RouteCollection;
|
|||||||
/* Rutas para tarifas */
|
/* Rutas para tarifas */
|
||||||
$routes->group('scripts', ['namespace' => 'App\Controllers\Scripts'], function ($routes) {
|
$routes->group('scripts', ['namespace' => 'App\Controllers\Scripts'], function ($routes) {
|
||||||
|
|
||||||
$routes->get('completar-identidades', 'UsersIntegrity::completarIdentidades');
|
//$routes->get('completar-identidades', 'UsersIntegrity::completarIdentidades');
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -3,6 +3,7 @@ namespace App\Controllers\Importadores;
|
|||||||
|
|
||||||
use App\Controllers\BaseResourceController;
|
use App\Controllers\BaseResourceController;
|
||||||
use App\Controllers\Presupuestos\Presupuestocliente;
|
use App\Controllers\Presupuestos\Presupuestocliente;
|
||||||
|
use App\Models\Presupuestos\PresupuestoModel;
|
||||||
use App\Services\PresupuestoService;
|
use App\Services\PresupuestoService;
|
||||||
|
|
||||||
class ImportadorBubok extends BaseResourceController
|
class ImportadorBubok extends BaseResourceController
|
||||||
@ -50,6 +51,73 @@ class ImportadorBubok extends BaseResourceController
|
|||||||
return view(static::$viewPath . 'viewImportadorBubokTool', $viewData);
|
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()
|
public function importarFila()
|
||||||
{
|
{
|
||||||
@ -83,7 +151,7 @@ class ImportadorBubok extends BaseResourceController
|
|||||||
'message' => 'Número de orden o ID del producto no reconocidos.'
|
'message' => 'Número de orden o ID del producto no reconocidos.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$refCliente = "$orderNumber - $productId";
|
$refCliente = "$orderNumber-$productId";
|
||||||
|
|
||||||
// Titulo
|
// Titulo
|
||||||
$titulo = $producto->title ?? null;
|
$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
|
// Generamos el objeto a importar
|
||||||
$dataToImport = [
|
$dataToImport = [
|
||||||
'selectedTirada' => $tirada,
|
'selectedTirada' => $tirada,
|
||||||
@ -295,8 +372,8 @@ class ImportadorBubok extends BaseResourceController
|
|||||||
'tipo' => '',
|
'tipo' => '',
|
||||||
'tipo_presupuesto_id' => $encuadernadoId,
|
'tipo_presupuesto_id' => $encuadernadoId,
|
||||||
'clienteId' => 40, // BUBOK ID
|
'clienteId' => 40, // BUBOK ID
|
||||||
'isColor' => ($interiorTipo === 'color') ? 1 : 0,
|
'isColor' => $isColor,
|
||||||
'isHq' => 0,
|
'isHq' => $isHq,
|
||||||
'paginas' => $paginas,
|
'paginas' => $paginas,
|
||||||
'paginasColor' => ($interiorTipo === 'color') ? $paginas : 0,
|
'paginasColor' => ($interiorTipo === 'color') ? $paginas : 0,
|
||||||
'paginasCuadernillo' => 32,
|
'paginasCuadernillo' => 32,
|
||||||
@ -317,15 +394,16 @@ class ImportadorBubok extends BaseResourceController
|
|||||||
'sobrecubierta' => [],
|
'sobrecubierta' => [],
|
||||||
'faja' => null,
|
'faja' => null,
|
||||||
|
|
||||||
'entrega_taller' => 1,
|
'direcciones' => $direcciones,
|
||||||
//'direcciones' => $direcciones, las direcciones que aparecen no se añaden, ya que la recogida la hacen ellos con su empresa de mensajeria
|
|
||||||
|
|
||||||
'ivaReducido' => 1,
|
'ivaReducido' => 1,
|
||||||
];
|
];
|
||||||
|
|
||||||
/*return $this->respond([
|
/*return $this->respond([
|
||||||
'status' => 400,
|
'status' => 400,
|
||||||
'message' => $dataToImport
|
'message' => $dataToImport,
|
||||||
|
'interiorTipo' => $interiorTipo,
|
||||||
|
'isColor' => $isColor
|
||||||
]);*/
|
]);*/
|
||||||
|
|
||||||
// 5. Guardar
|
// 5. Guardar
|
||||||
@ -358,11 +436,11 @@ class ImportadorBubok extends BaseResourceController
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// confirmar y crear pedido y ot
|
// confirmar y crear pedido y ot
|
||||||
$presupuestoModel->confirmarPresupuesto($response['sk_id']);
|
$presupuestoModel->confirmarPresupuesto($response['sk_id']);
|
||||||
PresupuestoService::crearPedido($response['sk_id'],isImported:true);
|
PresupuestoService::crearPedido($response['sk_id'], isImported: true);
|
||||||
|
|
||||||
|
|
||||||
if (!isset($response['sk_id'])) {
|
if (!isset($response['sk_id'])) {
|
||||||
return $this->respond([
|
return $this->respond([
|
||||||
|
|||||||
@ -12,7 +12,7 @@ return [
|
|||||||
'precio_compra' => 'Precio Compra',
|
'precio_compra' => 'Precio Compra',
|
||||||
'importar' => 'Importar',
|
'importar' => 'Importar',
|
||||||
'subirArchivoRama' => 'Cargar Excel proporcionado por RA-MA',
|
'subirArchivoRama' => 'Cargar Excel proporcionado por RA-MA',
|
||||||
'subirArchivoBubok' => 'Cargar XML proporcionado por BUBOK',
|
'subirArchivoBubok' => 'Cargar ZIP proporcionado por BUBOK',
|
||||||
|
|
||||||
'libro' => 'libro',
|
'libro' => 'libro',
|
||||||
'id' => 'ID',
|
'id' => 'ID',
|
||||||
|
|||||||
@ -22,8 +22,8 @@
|
|||||||
|
|
||||||
<div class="col-md-6 mb-3">
|
<div class="col-md-6 mb-3">
|
||||||
<label for="xmlFile"
|
<label for="xmlFile"
|
||||||
class="form-label"><?= lang('Importador.subirArchivoBubok') ?? 'Subir archivo XML' ?></label>
|
class="form-label"><?= lang('Importador.subirArchivoBubok') ?? 'Subir archivo ZIP' ?></label>
|
||||||
<input type="file" id="xmlFile" accept=".xml" class="form-control">
|
<input type="file" id="xmlFile" accept=".zip" class="form-control">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4 mb-3 d-flex align-items-end">
|
<div class="col-md-4 mb-3 d-flex align-items-end">
|
||||||
<button type="button" id="importBtn" class="btn btn-success w-100">
|
<button type="button" id="importBtn" class="btn btn-success w-100">
|
||||||
|
|||||||
@ -1,22 +1,66 @@
|
|||||||
document.addEventListener('DOMContentLoaded', function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
|
||||||
let dataTable;
|
let dataTable;
|
||||||
let productosOriginales = [];
|
let productosOriginales = [];
|
||||||
let datosComunesPedido = {};
|
let datosComunesPedido = {};
|
||||||
|
|
||||||
|
document.getElementById('importBtn').disabled = true;
|
||||||
|
|
||||||
document.getElementById('xmlFile').addEventListener('change', function (e) {
|
document.getElementById('xmlFile').addEventListener('change', function (e) {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file || !file.name.endsWith('.zip')) {
|
||||||
|
Swal.fire('Error', 'Selecciona un archivo .zip válido.', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('importBtn').disabled = true;
|
||||||
|
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (event) {
|
reader.onload = async function (event) {
|
||||||
const xmlText = event.target.result;
|
try {
|
||||||
parseXmlAndLoadTable(xmlText);
|
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/')
|
||||||
|
);
|
||||||
|
|
||||||
|
if (xmlFiles.length === 0) {
|
||||||
|
Swal.fire('Error', 'El ZIP no contiene archivos XML.', 'error');
|
||||||
|
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');
|
||||||
|
await parseXmlAndLoadTable(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
Swal.close();
|
||||||
|
Swal.fire('Éxito', `${xmlFiles.length} archivos XML cargados.`, 'success');
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
Swal.fire('Error', 'No se pudo procesar el ZIP.', 'error');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
reader.readAsText(file);
|
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
function parseXmlAndLoadTable(xmlText) {
|
async function parseXmlAndLoadTable(xmlText) {
|
||||||
let parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
let xmlDoc;
|
let xmlDoc;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -27,7 +71,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
datosComunesPedido = {
|
const pedidoActual = {
|
||||||
orderNumber: xmlDoc.querySelector('orderNumber')?.textContent ?? '',
|
orderNumber: xmlDoc.querySelector('orderNumber')?.textContent ?? '',
|
||||||
shipping: {
|
shipping: {
|
||||||
address: xmlDoc.querySelector('shippingData > address')?.textContent ?? '',
|
address: xmlDoc.querySelector('shippingData > address')?.textContent ?? '',
|
||||||
@ -41,9 +85,20 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const products = Array.from(xmlDoc.getElementsByTagName('product'));
|
const products = Array.from(xmlDoc.getElementsByTagName('product'));
|
||||||
productosOriginales = products.map((prod, idx) => ({ index: idx, data: prod }));
|
const offset = productosOriginales.length;
|
||||||
|
const rows = [];
|
||||||
|
|
||||||
|
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 id = product.querySelector('id')?.textContent ?? '';
|
const id = product.querySelector('id')?.textContent ?? '';
|
||||||
const title = product.querySelector('title')?.textContent ?? '';
|
const title = product.querySelector('title')?.textContent ?? '';
|
||||||
const pages = product.querySelector('body > pages')?.textContent ?? '';
|
const pages = product.querySelector('body > pages')?.textContent ?? '';
|
||||||
@ -69,69 +124,129 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return [
|
// Obtener referencia del pedido
|
||||||
`<input type="checkbox" class="form-check-input select-row" checked>`,
|
const refCliente = `${pedidoActual.orderNumber}-${id}`;
|
||||||
`${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')) {
|
// Validar fila con su propio pedido
|
||||||
const headerHtml = `
|
const result = await validarProductoXml(product, pedidoActual);
|
||||||
<thead>
|
|
||||||
<tr>
|
let checkboxHtml = '';
|
||||||
<th><input type="checkbox" id="selectAll" /></th>
|
let notaHtml = '';
|
||||||
<th>Referencia</th>
|
let actionBtnsHtml = '';
|
||||||
<th>Título</th>
|
|
||||||
<th>Tamaño</th>
|
if (result.apto) {
|
||||||
<th>Páginas</th>
|
checkboxHtml = `<input type="checkbox" class="form-check-input select-row" checked>`;
|
||||||
<th>Tirada</th>
|
notaHtml = '';
|
||||||
<th>Interior</th>
|
actionBtnsHtml = `
|
||||||
<th>Notas</th>
|
<div class="d-flex flex-column align-items-start">
|
||||||
<th>Acciones</th>
|
<button type="button" class="btn btn-outline-success btn-sm mb-1 importRow">
|
||||||
</tr>
|
<i class="fas fa-file-import me-1"></i> Importar
|
||||||
<tr>
|
</button>
|
||||||
<th></th><th></th><th></th><th></th><th></th>
|
<button type="button" class="btn btn-outline-danger btn-sm deleteRow">
|
||||||
<th></th><th></th><th></th><th></th>
|
<i class="fas fa-trash-alt me-1"></i> Eliminar
|
||||||
</tr>
|
</button>
|
||||||
</thead>`;
|
</div>
|
||||||
$('#xmlTable').html(headerHtml);
|
`;
|
||||||
|
} else {
|
||||||
|
checkboxHtml = `<input type="checkbox" class="form-check-input select-row" disabled>`;
|
||||||
|
notaHtml = `<span class="badge bg-danger">${result.reason}</span>`;
|
||||||
|
actionBtnsHtml = `
|
||||||
|
<div class="d-flex flex-column align-items-start">
|
||||||
|
<span class="badge rounded-pill bg-danger mb-2">No Apto</span>
|
||||||
|
<button type="button" class="btn btn-outline-danger btn-sm deleteRow">
|
||||||
|
<i class="fas fa-trash-alt me-1"></i> Eliminar
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
rows.push([checkboxHtml, refCliente, title, tamano, pages, tirada, interior, notaHtml, actionBtnsHtml]);
|
||||||
}
|
}
|
||||||
|
|
||||||
dataTable = $('#xmlTable').DataTable({
|
if (!$.fn.DataTable.isDataTable('#xmlTable')) {
|
||||||
destroy: true,
|
$('#xmlTable').html(`
|
||||||
data: rows,
|
<thead>
|
||||||
orderCellsTop: true,
|
<tr>
|
||||||
responsive: true,
|
<th><input type="checkbox" id="selectAll" /></th>
|
||||||
scrollX: true,
|
<th>Referencia</th>
|
||||||
dom: 'lfrtip',
|
<th>Título</th>
|
||||||
language: {
|
<th>Tamaño</th>
|
||||||
url: "/themes/vuexy/vendor/libs/datatables-sk/plugins/i18n/es-ES.json"
|
<th>Páginas</th>
|
||||||
},
|
<th>Tirada</th>
|
||||||
order: [[1, 'asc']]
|
<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 thead tr:eq(1) th').each(function (i) {
|
if (!dataTable) {
|
||||||
if (![0, 7, 8].includes(i)) {
|
dataTable = $('#xmlTable').DataTable({
|
||||||
$(this).html('<input type="text" class="form-control form-control-sm" placeholder="Filtrar..." />');
|
destroy: true,
|
||||||
$('input', this).on('keyup change', function () {
|
data: rows,
|
||||||
if (dataTable.column(i).search() !== this.value) {
|
orderCellsTop: true,
|
||||||
dataTable.column(i).search(this.value).draw();
|
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 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();
|
||||||
|
} 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': '<?= csrf_hash() ?>'
|
||||||
|
},
|
||||||
|
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() {
|
function setupEventListeners() {
|
||||||
$('#xmlTable tbody').off('click', '.deleteRow').on('click', '.deleteRow', function () {
|
$('#xmlTable tbody').off('click', '.deleteRow').on('click', '.deleteRow', function () {
|
||||||
dataTable.row($(this).parents('tr')).remove().draw();
|
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 () {
|
$('#xmlTable tbody').off('click', '.importRow').on('click', '.importRow', async function () {
|
||||||
const $row = $(this).closest('tr');
|
const $row = $(this).closest('tr');
|
||||||
const rowIndex = dataTable.row($row).index();
|
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 {
|
try {
|
||||||
const response = await fetch('/importador/bubok/importar-fila', {
|
const response = await fetch('/importador/bubok/importar-fila', {
|
||||||
@ -153,32 +271,46 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
producto: xmlToJson(xmlProduct),
|
producto: xmlToJson(xmlProduct),
|
||||||
pedido: datosComunesPedido
|
pedido: pedido
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
const rowData = dataTable.row($row).data();
|
const rowData = dataTable.row($row).data();
|
||||||
|
|
||||||
if (response.ok && result.status === 200) {
|
if (response.ok && result.status === 200) {
|
||||||
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
||||||
const htmlLink = skUrl
|
const skId = result.data?.sk_id ?? '';
|
||||||
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto</a>`
|
|
||||||
: 'Importado';
|
rowData[7] = skUrl
|
||||||
rowData[7] = htmlLink;
|
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto (${skId})</a>`
|
||||||
|
: `<span class="badge bg-success">Importado (${skId})</span>`;
|
||||||
|
|
||||||
dataTable.row($row).data(rowData).draw(false);
|
dataTable.row($row).data(rowData).draw(false);
|
||||||
|
|
||||||
|
let html = skUrl
|
||||||
|
? `La fila se importó exitosamente.<br><br><a href="${skUrl}" target="_blank" class="btn btn-primary mt-2">Ver presupuesto (${skId})</a>`
|
||||||
|
: `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.<br><br><a href="${skUrl}" target="_blank" class="btn btn-primary mt-2">Ver presupuesto (${skId})</a>`
|
||||||
|
: `La fila se importó con advertencia de coste. (${skId})`;
|
||||||
|
icon = 'warning';
|
||||||
|
}
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Importado correctamente',
|
title: 'Importado correctamente',
|
||||||
html: skUrl
|
html: html,
|
||||||
? `Se importó correctamente.<br><a href="${skUrl}" target="_blank" class="btn btn-primary mt-2">Ver presupuesto</a>`
|
icon: icon,
|
||||||
: 'Importación realizada.',
|
|
||||||
icon: 'success',
|
|
||||||
confirmButtonText: 'Aceptar',
|
confirmButtonText: 'Aceptar',
|
||||||
customClass: { confirmButton: 'btn btn-success' }
|
customClass: { confirmButton: 'btn btn-success' }
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error inesperado'}</span>`;
|
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error desconocido'}</span>`;
|
||||||
dataTable.row($row).data(rowData).draw(false);
|
dataTable.row($row).data(rowData).draw(false);
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
@ -189,15 +321,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
customClass: { confirmButton: 'btn btn-danger' }
|
customClass: { confirmButton: 'btn btn-danger' }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
dataTable.row($row).data(rowData).draw(false);
|
console.error('Importación fallida:', error);
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
Swal.fire('Error', 'Error de comunicación con el servidor.', 'error');
|
Swal.fire('Error', 'Error de comunicación con el servidor.', 'error');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$('#selectAll').off('change').on('change', function () {
|
$('#selectAll').off('change').on('change', function () {
|
||||||
const checked = $(this).is(':checked');
|
const checked = $(this).is(':checked');
|
||||||
$('#xmlTable tbody input.select-row:enabled').prop('checked', checked);
|
$('#xmlTable tbody input.select-row:enabled').prop('checked', checked);
|
||||||
@ -258,9 +389,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const filasSeleccionadas = [];
|
const filasSeleccionadas = [];
|
||||||
|
|
||||||
dataTable.rows().every(function () {
|
dataTable.rows().every(function () {
|
||||||
const node = this.node();
|
const node = this.node();
|
||||||
const checkbox = $(node).find('input.select-row');
|
const checkbox = $(node).find('input.select-row');
|
||||||
@ -268,7 +399,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
filasSeleccionadas.push(this.index());
|
filasSeleccionadas.push(this.index());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (filasSeleccionadas.length === 0) {
|
if (filasSeleccionadas.length === 0) {
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Atención',
|
title: 'Atención',
|
||||||
@ -279,7 +410,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: '¿Importar seleccionados?',
|
title: '¿Importar seleccionados?',
|
||||||
text: `Se van a importar ${filasSeleccionadas.length} filas.`,
|
text: `Se van a importar ${filasSeleccionadas.length} filas.`,
|
||||||
@ -294,11 +425,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
}).then(async (result) => {
|
}).then(async (result) => {
|
||||||
if (!result.isConfirmed) return;
|
if (!result.isConfirmed) return;
|
||||||
|
|
||||||
for (const i of filasSeleccionadas) {
|
for (const i of filasSeleccionadas) {
|
||||||
const productXml = productosOriginales.find(p => p.index === i)?.data;
|
const productoObj = productosOriginales.find(p => p.index === i);
|
||||||
if (!productXml) continue;
|
if (!productoObj) continue;
|
||||||
|
|
||||||
|
const productXml = productoObj.data;
|
||||||
|
const pedido = productoObj.pedido;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/importador/bubok/importar-fila', {
|
const response = await fetch('/importador/bubok/importar-fila', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -308,29 +442,30 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
producto: xmlToJson(productXml),
|
producto: xmlToJson(productXml),
|
||||||
pedido: datosComunesPedido
|
pedido: pedido
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
const rowNode = dataTable.row(i).node();
|
const rowNode = dataTable.row(i).node();
|
||||||
const rowData = dataTable.row(i).data();
|
const rowData = dataTable.row(i).data();
|
||||||
|
|
||||||
if (response.ok && result.status === 200) {
|
if (response.ok && result.status === 200) {
|
||||||
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
const skUrl = result.data?.sk_url?.replace('presupuestocliente', 'presupuestoadmin') ?? null;
|
||||||
|
const skId = result.data?.sk_id ?? '';
|
||||||
rowData[7] = skUrl
|
rowData[7] = skUrl
|
||||||
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto</a>`
|
? `<a href="${skUrl}" target="_blank" class="btn btn-sm btn-primary">Ver presupuesto (${skId})</a>`
|
||||||
: '<span class="badge bg-success">Importado</span>';
|
: `<span class="badge bg-success">Importado (${skId})</span>`;
|
||||||
} else {
|
} else {
|
||||||
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error desconocido'}</span>`;
|
rowData[7] = `<span class="badge bg-danger">${result.message ?? 'Error desconocido'}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
dataTable.row(rowNode).data(rowData).draw(false);
|
dataTable.row(rowNode).data(rowData).draw(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Importación fallida:', error);
|
console.error('Importación fallida:', error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Swal.fire({
|
Swal.fire({
|
||||||
title: 'Importación finalizada',
|
title: 'Importación finalizada',
|
||||||
text: 'Se han procesado todas las filas seleccionadas.',
|
text: 'Se han procesado todas las filas seleccionadas.',
|
||||||
@ -340,5 +475,5 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user