From b288ca498cf42a2d1b93282201bde5dfbf43823d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Jim=C3=A9nez?= Date: Tue, 29 Apr 2025 19:30:35 +0200 Subject: [PATCH 1/5] terminado --- .../Importadores/ImportadorCatalogo.php | 23 +++--- .../Presupuestos/Presupuestocliente.php | 77 +++++++++++++++---- ...5-04-29-180600_AddClienteNoBasePODnoHQ.php | 30 ++++++++ ci4/app/Entities/Clientes/ClienteEntity.php | 4 + ci4/app/Language/es/Clientes.php | 2 + ci4/app/Models/Clientes/ClienteModel.php | 2 + ci4/app/Services/EmailService.php | 2 +- .../Services/PresupuestoClienteService.php | 2 +- ci4/app/Services/PresupuestoService.php | 51 ++++++++++++ .../clientes/cliente/_clienteFormItems.php | 22 ++++++ .../importadores/catalogo/catalogo_tool.js | 34 ++++++-- 11 files changed, 215 insertions(+), 34 deletions(-) create mode 100644 ci4/app/Database/Migrations/2025-04-29-180600_AddClienteNoBasePODnoHQ.php diff --git a/ci4/app/Controllers/Importadores/ImportadorCatalogo.php b/ci4/app/Controllers/Importadores/ImportadorCatalogo.php index 30386acf..f945ccb3 100644 --- a/ci4/app/Controllers/Importadores/ImportadorCatalogo.php +++ b/ci4/app/Controllers/Importadores/ImportadorCatalogo.php @@ -5,6 +5,7 @@ use App\Controllers\BaseResourceController; use App\Entities\Catalogo\CatalogoLibroEntity; use App\Models\Catalogo\CatalogoLibroModel; use App\Controllers\Presupuestos\Presupuestocliente; +use App\Services\PresupuestoService; class ImportadorCatalogo extends BaseResourceController { @@ -281,17 +282,19 @@ class ImportadorCatalogo extends BaseResourceController ] ]; - // Ajuste del precio a RAMA - $dataToUpdate = [ - 'total_aceptado' => ($tirada * $precio_compra), - 'total_aceptado_revisado' => ($tirada * $precio_compra), - 'total_precio_unidad' => $precio_compra - ]; - $presupuestoModel = model('App\Models\Presupuestos\Presupuestomodel'); - $presupuestoModel->update($response['data']['sk_id'], $dataToUpdate); - - + // Ajuste del precio a RAMA + $respuesta_ajuste = PresupuestoService::ajustarPresupuesto( + $response['data']['sk_id'], + $precio_compra, + $tirada + ); + if ($respuesta_ajuste['warning'] == true) { + $response['price_warning'] = [ + 'new_precio_unidad' => $respuesta_ajuste['new_precio_unidad'], + 'new_total' => $respuesta_ajuste['new_total'], + ]; + } return $this->respond($response); diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index d6bce82f..4ce82823 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -330,10 +330,16 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'a_favor_fibra' => 1, ); + $cliente_model = model(('App\Models\Clientes\ClienteModel')); + $cliente = $cliente_model->find($cliente_id); // Para POD siempre es HQ - if ($tirada[0] <= $POD) { + if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) { $isHq = true; } + $forzarRotativa = false; + if ($tirada[0] <= $POD && $cliente->forzar_rotativa_pod) { + $forzarRotativa = true; + } $input_data = array( 'uso' => 'interior', @@ -346,7 +352,8 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'cliente_id' => $cliente_id, 'paginas_color' => $paginas_color, 'excluirRotativa' => $excluirRotativa, - 'papelInteriorDiferente' => $papelInteriorDiferente + 'papelInteriorDiferente' => $papelInteriorDiferente, + 'forzarRotativa' => $forzarRotativa, ); $interior = PresupuestoClienteService::obtenerInterior($input_data); @@ -460,6 +467,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $id = $reqData['id'] ?? 0; $cliente_id = $reqData['clienteId'] ?? -1; + $noEnvioBase = model('App\Models\Clientes\ClienteModel')->find($cliente_id)->no_envio_base ?? false; $tirada = $reqData['tirada'] ?? 0; $selectedTirada = $reqData['selectedTirada'] ?? -1; @@ -609,6 +617,10 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } else { $coste = floatval($coste_direccion->coste); $margen = $coste * (intval($coste_direccion->margen) / 100.0); + if ($noEnvioBase) { + $coste = 0.0; + $margen = 0.0; + } $return_data['eb'][$i] = round($coste + $margen, 2); } } @@ -746,10 +758,16 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'a_favor_fibra' => 1, ); + $cliente_model = model(('App\Models\Clientes\ClienteModel')); + $cliente = $cliente_model->find($cliente_id); // Para POD siempre es HQ - if ($tirada[0] <= $POD) { + if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) { $isHq = true; } + $forzarRotativa = false; + if ($tirada[0] <= $POD && $cliente->forzar_rotativa_pod) { + $forzarRotativa = true; + } $input_data = array( 'uso' => 'interior', @@ -762,7 +780,8 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'cliente_id' => $cliente_id, 'paginas_color' => $paginas_color, 'excluirRotativa' => $excluirRotativa, - 'papelInteriorDiferente' => $papelInteriorDiferente + 'papelInteriorDiferente' => $papelInteriorDiferente, + 'forzarRotativa' => $forzarRotativa, ); $interior = PresupuestoClienteService::obtenerInterior($input_data); @@ -1126,6 +1145,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $peso_libro = $resultado_presupuesto['peso'][array_search($selected_tirada, $tirada)]; // calculo del envio base (tirada_maxima) + $noEnvioBase = model('App\Models\Clientes\ClienteModel')->find($cliente_id)->no_envio_base ?? false; $resultado_presupuesto['eb'] = []; $datos_presupuesto['envio_base'] = 0; for ($i = 0; $i < count($tirada); $i++) { @@ -1139,6 +1159,10 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController ); if (intval($selected_tirada) == intval($tirada[$i])) { + if ($noEnvioBase) { + $coste_direccion->coste = 0.0; + $coste_direccion->margen = 0.0; + } $datos_presupuesto['envio_base'] = round($coste_direccion->coste * (1 + $coste_direccion->margen / 100.0), 2); } @@ -1160,6 +1184,10 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController ]; return $resultado_presupuesto; } else { + if ($noEnvioBase) { + $coste_direccion->coste = 0.0; + $coste_direccion->margen = 0.0; + } $resultado_presupuesto['eb'][$i] = round($coste_direccion->coste, 2); $resultado_presupuesto['eb_margen'][$i] = round($coste_direccion->margen, 2); } @@ -1176,10 +1204,10 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController for ($i = 0; $i < count($tirada); $i++) { $coste_envio = 0.0; $coste_envio += ($resultado_presupuesto['eb'][$i] / $tirada[$i]); - $resultado_presupuesto['info']['totales'][$i]['envio_base_margen'] = - floatval($resultado_presupuesto['eb'][$i])*(floatval($resultado_presupuesto['eb_margen'][$i])/100.0); + $resultado_presupuesto['info']['totales'][$i]['envio_base_margen'] = + floatval($resultado_presupuesto['eb'][$i]) * (floatval($resultado_presupuesto['eb_margen'][$i]) / 100.0); $resultado_presupuesto['info']['totales'][$i]['envio_base_coste'] = $resultado_presupuesto['eb'][$i]; - + $resultado_presupuesto['precio_u'][$i] = round(floatval($resultado_presupuesto['precio_u'][$i]) + $coste_envio, 4); } @@ -1357,7 +1385,11 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController foreach ($serviciosAcabado as $service) { $model = model('App\Models\Presupuestos\PresupuestoAcabadosModel'); $servicio = $model->getPrecioTarifa( - intval($service), intval($selected_tirada)+$resultado_presupuesto['info']['merma'], -1, $POD); + intval($service), + intval($selected_tirada) + $resultado_presupuesto['info']['merma'], + -1, + $POD + ); if (count($servicio) > 0) { if ($servicio[0]->total > 0) { @@ -1375,8 +1407,11 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController foreach ($serviciosAcabado as $service) { $model = model('App\Models\Presupuestos\PresupuestoAcabadosModel'); $servicio = $model->getPrecioTarifa( - intval($service), - intval($selected_tirada) + $resultado_presupuesto['info']['merma'], -1, $POD); + intval($service), + intval($selected_tirada) + $resultado_presupuesto['info']['merma'], + -1, + $POD + ); if (count($servicio) > 0) { if ($servicio[0]->total > 0) { @@ -1393,8 +1428,11 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController foreach ($serviciosAcabado as $service) { $model = model('App\Models\Presupuestos\PresupuestoAcabadosModel'); $servicio = $model->getPrecioTarifa( - intval($service), - intval($selected_tirada) + $resultado_presupuesto['info']['merma'], -1, $POD); + intval($service), + intval($selected_tirada) + $resultado_presupuesto['info']['merma'], + -1, + $POD + ); if (count($servicio) > 0) { if ($servicio[0]->total > 0) { @@ -1407,7 +1445,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $tarifa_id = model('App\Models\Configuracion\ConfigVariableModel')->getVariable('id_servicio_lomo_redondo')->value; $serv_lomo = PresupuestoCLienteService::getServiciosManipulado([ 'tarifa_id' => intval($tarifa_id), - 'tirada' => $selected_tirada+$resultado_presupuesto['info']['merma'], + 'tirada' => $selected_tirada + $resultado_presupuesto['info']['merma'], 'POD' => $POD, ])[0]; @@ -2039,10 +2077,16 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $info['merma'] = $datosPedido->merma; } + $cliente_model = model(('App\Models\Clientes\ClienteModel')); + $cliente = $cliente_model->find($cliente_id); // Para POD siempre es HQ - if ($tirada[$t] <= $POD) { + if ($tirada[$t] <= $POD && !$cliente->forzar_rotativa_pod) { $isHq = true; } + $forzarRotativa = false; + if ($tirada[$t] <= $POD && $cliente->forzar_rotativa_pod) { + $forzarRotativa = true; + } $input_data = array( 'uso' => 'interior', @@ -2055,7 +2099,8 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'cliente_id' => $cliente_id, 'paginas_color' => $paginas_color, 'excluirRotativa' => $excluirRotativa, - 'papelInteriorDiferente' => $papelInteriorDiferente + 'papelInteriorDiferente' => $papelInteriorDiferente, + 'forzarRotativa' => $forzarRotativa, ); $interior = PresupuestoClienteService::obtenerInterior($input_data); @@ -3071,7 +3116,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $sumForFactor += round($linea['precio_pedido'], 2) - round($linea['margen_papel_pedido'], 2); $margenPapel += round($linea['margen_papel_pedido'], 2); - + $totalImpresion += round($linea['precio_click_pedido'], 2); $totalImpresion -= round($linea['margen_click_pedido'], 2); $sumForFactor += round($linea['precio_click_pedido'], 2) diff --git a/ci4/app/Database/Migrations/2025-04-29-180600_AddClienteNoBasePODnoHQ.php b/ci4/app/Database/Migrations/2025-04-29-180600_AddClienteNoBasePODnoHQ.php new file mode 100644 index 00000000..161c00f6 --- /dev/null +++ b/ci4/app/Database/Migrations/2025-04-29-180600_AddClienteNoBasePODnoHQ.php @@ -0,0 +1,30 @@ + [ + "type" => "TINYINT", + "default" => 0, + ], + "forzar_rotativa_pod" => [ + "type" => "TINYINT", + "default" => 0, + ], + ]; + public function up() + { + $this->forge->addColumn('clientes', $this->COLUMNS); + } + + public function down() + { + // + $this->forge->dropColumn('clientes', array_keys($this->COLUMNS)); + } +} diff --git a/ci4/app/Entities/Clientes/ClienteEntity.php b/ci4/app/Entities/Clientes/ClienteEntity.php index 5ea34297..d97b60b1 100755 --- a/ci4/app/Entities/Clientes/ClienteEntity.php +++ b/ci4/app/Entities/Clientes/ClienteEntity.php @@ -48,6 +48,8 @@ class ClienteEntity extends \CodeIgniter\Entity\Entity "updated_at" => null, "user_created_id" => 1, "user_update_id" => 1, + "no_envio_base" => 0, + "forzar_rotativa_pod" => 0, ]; protected $casts = [ "comunidad_autonoma_id" => "?int", @@ -70,6 +72,8 @@ class ClienteEntity extends \CodeIgniter\Entity\Entity "is_deleted" => "int", "user_created_id" => "int", "user_update_id" => "int", + "no_envio_base" => "boolean", + "forzar_rotativa_pod" => "boolean", ]; public function comercial() : ?UserEntity diff --git a/ci4/app/Language/es/Clientes.php b/ci4/app/Language/es/Clientes.php index 3e505bbb..b236c7d8 100755 --- a/ci4/app/Language/es/Clientes.php +++ b/ci4/app/Language/es/Clientes.php @@ -46,6 +46,8 @@ return [ 'userCreatedId' => 'User Created ID', 'userUpdateId' => 'User Update ID', 'vencimiento' => 'Vencimiento', + 'removeEnvioBase' => 'No añadir Envio Base', + 'forzarRotativaPod' => 'Forzar rotativa en POD', 'direccionesEnvio' => 'Direcciones de Envío', diff --git a/ci4/app/Models/Clientes/ClienteModel.php b/ci4/app/Models/Clientes/ClienteModel.php index 57de29a4..16a7ce91 100755 --- a/ci4/app/Models/Clientes/ClienteModel.php +++ b/ci4/app/Models/Clientes/ClienteModel.php @@ -61,6 +61,8 @@ class ClienteModel extends \App\Models\BaseModel "comentarios", "user_created_id", "user_update_id", + "no_envio_base", + "forzar_rotativa_pod", ]; protected $returnType = ClienteEntity::class; protected $useSoftDeletes = true; diff --git a/ci4/app/Services/EmailService.php b/ci4/app/Services/EmailService.php index 3e9a63e5..95cfd5b0 100755 --- a/ci4/app/Services/EmailService.php +++ b/ci4/app/Services/EmailService.php @@ -9,7 +9,7 @@ class EmailService public function send(string $subject, string $body, $recipient): bool { - $skEnv = env('sk_environment', 'production'); // fallback a producción si no está definido + $skEnv = env('SK_ENVIRONMENT', 'production'); // fallback a producción si no está definido // Si no estamos en producción, forzar el destinatario a uno fijo if ($skEnv !== 'production') { diff --git a/ci4/app/Services/PresupuestoClienteService.php b/ci4/app/Services/PresupuestoClienteService.php index 6cc202df..21dc68d1 100755 --- a/ci4/app/Services/PresupuestoClienteService.php +++ b/ci4/app/Services/PresupuestoClienteService.php @@ -58,7 +58,7 @@ class PresupuestoClienteService extends BaseService if ($total_plana < 0 && $total_rotativa < 0) return []; else { - if ($total_plana > $total_rotativa) + if ($total_plana > $total_rotativa && $data['forzarRotativa'] == 0) return $plana; else return [$rotativa]; diff --git a/ci4/app/Services/PresupuestoService.php b/ci4/app/Services/PresupuestoService.php index 0c46362a..de6986af 100755 --- a/ci4/app/Services/PresupuestoService.php +++ b/ci4/app/Services/PresupuestoService.php @@ -1983,4 +1983,55 @@ class PresupuestoService extends BaseService return $peso; } + + public static function ajustarPresupuesto($id, $precio_unidad = null, $unidades = null, $precio_total = null){ + + $precio_total_asignado = 0; + $precio_unidad_asignado = $precio_unidad; + $warning = false; + + $model = model('App\Models\Presupuestos\PresupuestoModel'); + if($precio_unidad != null && $unidades != null){ + $precio_total_asignado = round(floatval($precio_unidad) * intval($unidades), 2); + } + else{ + $precio_total_asignado = floatval($precio_total); + } + $presupuesto = $model->find($id); + $costes = floatval($presupuesto->total_costes); + $envio_base = floatval($presupuesto->envio_base); + if($costes + $envio_base > $precio_total_asignado){ + $precio_total_asignado = round($costes + $envio_base, 2); + $precio_unidad_asignado = round($precio_total_asignado / intval($unidades), 4); + $warning = true; + } + $total_margenes = $precio_total_asignado - $costes - $envio_base; + + $sumForFactor = floatval($presupuesto->total_coste_papel) + floatval($presupuesto->total_coste_impresion); + $sumForFactorPonderado = $sumForFactor + floatval($presupuesto->total_coste_servicios); + + $factor = ($precio_total_asignado - floatval($presupuesto->envio_base) + - floatval($presupuesto->total_coste_envios) - floatval($presupuesto->total_margen_envios)) / $sumForFactor; + + $factorPonderado = ($precio_total_asignado - floatval($presupuesto->envio_base) + - floatval($presupuesto->total_coste_envios) - floatval($presupuesto->total_margen_envios)) / $sumForFactorPonderado; + + if ($presupuesto) { + + $presupuesto->total_margenes = $total_margenes; + $presupuesto->total_aceptado = $precio_total_asignado; + $presupuesto->total_aceptado_revisado = $precio_total_asignado; + $presupuesto->total_antes_descuento = $precio_total_asignado; + $presupuesto->total_precio_unidad = $precio_unidad_asignado; + $presupuesto->total_factor = round($factor, 2); + $presupuesto->total_factor_ponderado = round($factorPonderado, 2); + $model->update($id, $presupuesto); + } + return [ + "success" => true, + "warning" => $warning, + "new_total" => $precio_total_asignado, + "new_precio_unidad" => $precio_unidad_asignado, + ]; + } } diff --git a/ci4/app/Views/themes/vuexy/form/clientes/cliente/_clienteFormItems.php b/ci4/app/Views/themes/vuexy/form/clientes/cliente/_clienteFormItems.php index fd9280ac..3bd3e733 100755 --- a/ci4/app/Views/themes/vuexy/form/clientes/cliente/_clienteFormItems.php +++ b/ci4/app/Views/themes/vuexy/form/clientes/cliente/_clienteFormItems.php @@ -521,6 +521,28 @@ +
+
+ +
+
+
+
+ +
+
+ +
+ +endSection() ?> + + + +section('css') ?> +"> + +endSection() ?> + + +section('additionalExternalJs') ?> + + + + + + + + + +endSection() ?> \ No newline at end of file diff --git a/ci4/app/Views/themes/vuexy/form/importador/catalogo/viewImportadorCatalogoTool.php b/ci4/app/Views/themes/vuexy/form/importador/catalogo/viewImportadorCatalogoTool.php index c5a008f2..2b1b3ea5 100644 --- a/ci4/app/Views/themes/vuexy/form/importador/catalogo/viewImportadorCatalogoTool.php +++ b/ci4/app/Views/themes/vuexy/form/importador/catalogo/viewImportadorCatalogoTool.php @@ -22,7 +22,7 @@
+ class="form-label">
diff --git a/ci4/app/Views/themes/vuexy/main/menus/importadores_menu.php b/ci4/app/Views/themes/vuexy/main/menus/importadores_menu.php index 8abad150..f7572e90 100644 --- a/ci4/app/Views/themes/vuexy/main/menus/importadores_menu.php +++ b/ci4/app/Views/themes/vuexy/main/menus/importadores_menu.php @@ -20,7 +20,14 @@ if (auth()->user()->can('importadores.menu')) { - + user()->can('importadores.bubok')) { ?> + + + diff --git a/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js b/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js new file mode 100644 index 00000000..d18f0f89 --- /dev/null +++ b/httpdocs/assets/js/safekat/pages/importadores/bubok/bubok_tool.js @@ -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 [ + ``, + `${datosComunesPedido.orderNumber} - ${id}`, + title, + tamano, + pages, + tirada, + interior, + '', + ` + ` + ]; + }); + + if (!$.fn.DataTable.isDataTable('#xmlTable')) { + const headerHtml = ` + + + + Referencia + Título + Tamaño + Páginas + Tirada + Interior + Notas + Acciones + + + + + + `; + $('#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', 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': '' + }, + 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 + ? `Ver presupuesto` + : 'Importado'; + rowData[7] = htmlLink; + dataTable.row($row).data(rowData).draw(false); + + Swal.fire({ + title: 'Importado correctamente', + html: skUrl + ? `Se importó correctamente.
Ver presupuesto` + : 'Importación realizada.', + icon: 'success', + confirmButtonText: 'Aceptar', + customClass: { confirmButton: 'btn btn-success' } + }); + } else { + rowData[7] = `${result.message ?? 'Error inesperado'}`; + 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': '' + }, + 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 + ? `Ver presupuesto` + : 'Importado'; + } 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.', + icon: 'success', + confirmButtonText: 'Aceptar', + customClass: { confirmButton: 'btn btn-success' } + }); + }); + }); + +}); From 91819052336710837ad3e9cb6665fccd9814aaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Jim=C3=A9nez?= Date: Wed, 30 Apr 2025 20:23:45 +0200 Subject: [PATCH 5/5] terminado --- .../Importadores/ImportadorCatalogo.php | 10 +++- .../Presupuestos/Presupuestoadmin.php | 39 +++++++++++++++ .../Presupuestos/Presupuestocliente.php | 35 ++----------- ci4/app/Services/PresupuestoService.php | 49 +++++++++++++++++-- .../importadores/catalogo/catalogo_tool.js | 6 +-- .../sections/lineasPresupuesto.js | 6 +++ 6 files changed, 106 insertions(+), 39 deletions(-) diff --git a/ci4/app/Controllers/Importadores/ImportadorCatalogo.php b/ci4/app/Controllers/Importadores/ImportadorCatalogo.php index c2e7eefc..e2ebb080 100644 --- a/ci4/app/Controllers/Importadores/ImportadorCatalogo.php +++ b/ci4/app/Controllers/Importadores/ImportadorCatalogo.php @@ -239,6 +239,8 @@ class ImportadorCatalogo extends BaseResourceController 'lomoRedondo' => 0 ], + 'ivaReducido' => 1, + 'guardas' => [], 'sobrecubierta' => $sobrecubierta, //'faja' => null, @@ -284,7 +286,9 @@ class ImportadorCatalogo extends BaseResourceController $respuesta_ajuste = PresupuestoService::ajustarPresupuesto( $response['data']['sk_id'], $precio_compra, - $tirada + $tirada, + null, + true ); if ($respuesta_ajuste['warning'] == true) { $response['price_warning'] = [ @@ -293,6 +297,10 @@ class ImportadorCatalogo extends BaseResourceController ]; } + // confirmar y crear pedido y ot + model('App\Models\Presupuestos\PresupuestoModel')->confirmarPresupuesto($response['data']['sk_id']); + PresupuestoService::crearPedido($response['data']['sk_id']); + return $this->respond($response); } catch (\Exception $e) { diff --git a/ci4/app/Controllers/Presupuestos/Presupuestoadmin.php b/ci4/app/Controllers/Presupuestos/Presupuestoadmin.php index 5fe536da..28e87c50 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestoadmin.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestoadmin.php @@ -993,6 +993,8 @@ class Presupuestoadmin extends \App\Controllers\BaseResourceController $reqData = $this->request->getPost(); + $calcular_merma = $reqData['calcular_merma'] ?? 0; + $type = $reqData['type'] ?? null; // por defecto, se deja cosido tapa blanda por ahora JJO $tipo_impresion_id = $reqData['tipo_impresion_id'] ?? 4; @@ -1071,8 +1073,27 @@ class Presupuestoadmin extends \App\Controllers\BaseResourceController 'a_favor_fibra' => $a_favor_fibra ); + $resourceData = PresupuestoService::obtenerComparadorPlana($input_data); + if($calcular_merma == 1 && count($resourceData) > 0 && + count($resourceData[0]['fields']) >0 && $resourceData[0]['fields']['num_formas'] > 0) { + + usort($resourceData, function ($a, $b) { + return $b['fields']['total_impresion'] <=> $a['fields']['total_impresion']; + }); + + $num_formas = []; + $formas_linea = $datosPedido->isCosido ? intval($resourceData[0]['fields']['num_formas']['value']) / 2 : + intval($resourceData[0]['fields']['num_formas']['value']); + array_push($num_formas, $formas_linea); + + $POD = $this->getPOD(); + $datosPedido->merma = PresupuestoService::calcular_merma($datosPedido->tirada,$POD, $num_formas); + + $resourceData = PresupuestoService::obtenerComparadorPlana($input_data); + } + } else if ($type == 'interior_rot') { $paginas = (object) array( @@ -1105,6 +1126,24 @@ class Presupuestoadmin extends \App\Controllers\BaseResourceController $resourceData = PresupuestoService::obtenerComparadorRotativa($input_data); + if($calcular_merma == 1 && count($resourceData) > 0 && + count($resourceData[0]['fields']) >0 && $resourceData[0]['fields']['num_formas'] > 0) { + + usort($resourceData, function ($a, $b) { + return $b['fields']['total_impresion'] <=> $a['fields']['total_impresion']; + }); + + $num_formas = []; + $formas_linea = $datosPedido->isCosido ? intval($resourceData[0]['fields']['num_formas']['value']) / 2 : + intval($resourceData[0]['fields']['num_formas']['value']); + array_push($num_formas, $formas_linea); + + $POD = $this->getPOD(); + $datosPedido->merma = PresupuestoService::calcular_merma($datosPedido->tirada,$POD, $num_formas); + + $resourceData = PresupuestoService::obtenerComparadorRotativa($input_data); + } + } else if ($type == 'cubierta' || $type == 'sobrecubierta' || $type == 'faja') { $datosPedido->solapas = $reqData['solapas']; diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index 4ce82823..6c0babdc 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -323,7 +323,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $datosPedido = (object) array( 'paginas' => $paginas, 'tirada' => $tirada[0], - 'merma' => $this->calcular_merma($tirada[0], $POD), + 'merma' => PresupuestoService::calcular_merma($tirada[0], $POD), 'ancho' => intval($tamanio['ancho']) ?? 100000, 'alto' => intval($tamanio['alto']) ?? 100000, 'isCosido' => $is_cosido, @@ -751,7 +751,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $datosPedido = (object) array( 'paginas' => $paginas, 'tirada' => $tirada[0], - 'merma' => $tirada[0] > $POD ? $this->calcular_merma($tirada[0], $POD) : 0, + 'merma' => $tirada[0] > $POD ? PresupuestoService::calcular_merma($tirada[0], $POD) : 0, 'ancho' => intval($tamanio['ancho']) ?? 100000, 'alto' => intval($tamanio['alto']) ?? 100000, 'isCosido' => $is_cosido, @@ -1340,7 +1340,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $datos_presupuesto['entrega_taller'] = $reqData['entrega_taller'] ?? 0; - $resultado_presupuesto['info']['merma'] = $this->calcular_merma($selected_tirada, $POD); + $resultado_presupuesto['info']['merma'] = PresupuestoService::calcular_merma($selected_tirada, $POD); $datos_presupuesto['faja'] = $faja; @@ -2067,7 +2067,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $datosPedido = (object) array( 'paginas' => $paginas, 'tirada' => $tirada[$t], - 'merma' => $this->calcular_merma($tirada[$t], $POD), + 'merma' => PresupuestoService::calcular_merma($tirada[$t], $POD), 'ancho' => intval($tamanio['ancho']) ?? 100000, 'alto' => intval($tamanio['alto']) ?? 100000, 'isCosido' => $is_cosido, @@ -2152,7 +2152,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController array_push($num_formas, $formas_linea); } } - $input_data['datosPedido']->merma = $this->calcular_merma($tirada[$t], $POD, $num_formas); + $input_data['datosPedido']->merma = PresupuestoService::calcular_merma($tirada[$t], $POD, $num_formas); if ($extra_info) { $info['merma'] = max($info['merma'], $input_data['datosPedido']->merma); } @@ -3187,31 +3187,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } } - - protected function calcular_merma($tirada, $POD, $formas_lineas_interior = []) - { - - $merma = 0; - - if ($tirada > $POD) { - $merma = $tirada * 0.1; - } else { - $merma_lineas = []; - foreach ($formas_lineas_interior as $formas_linea) { - if ($formas_linea > $tirada) - array_push($merma_lineas, $formas_linea - $tirada); - else - array_push($merma_lineas, $tirada % $formas_linea); - } - if (count($merma_lineas) > 0) - $merma = max($merma_lineas); - } - - - return round($merma, 0); - } - - protected function getPapelFormatoListItems($selId = null) { $papelFormatoModel = model('App\Models\Configuracion\PapelFormatoModel'); diff --git a/ci4/app/Services/PresupuestoService.php b/ci4/app/Services/PresupuestoService.php index 11393cfd..6feb5b4f 100755 --- a/ci4/app/Services/PresupuestoService.php +++ b/ci4/app/Services/PresupuestoService.php @@ -1984,7 +1984,7 @@ class PresupuestoService extends BaseService return $peso; } - public static function ajustarPresupuesto($id, $precio_unidad = null, $unidades = null, $precio_total = null){ + public static function ajustarPresupuesto($id, $precio_unidad = null, $unidades = null, $precio_total = null, $forzar_descuento = false){ $precio_total_asignado = 0; $precio_unidad_asignado = $precio_unidad; @@ -2000,12 +2000,24 @@ class PresupuestoService extends BaseService $presupuesto = $model->find($id); $costes = floatval($presupuesto->total_costes); $envio_base = floatval($presupuesto->envio_base); + + $total_descuento = 0; + $total_descuentoPercent = 0; if($costes + $envio_base > $precio_total_asignado){ - $precio_total_asignado = round($costes + $envio_base, 2); - $precio_unidad_asignado = round($precio_total_asignado / intval($unidades), 4); + + if($forzar_descuento){ + $total_descuento = $costes + $envio_base - $precio_total_asignado; + $total_descuentoPercent = round($total_descuento / ($costes + $envio_base) * 100, 2); + } + else{ + $precio_total_asignado = round($costes + $envio_base, 2); + $precio_unidad_asignado = round($precio_total_asignado / intval($unidades), 4); + } $warning = true; } - $total_margenes = $precio_total_asignado - $costes - $envio_base; + $total_margenes = $precio_total_asignado - $costes - $envio_base < 0 ? + 0 : + $precio_total_asignado - $costes - $envio_base; $sumForFactor = floatval($presupuesto->total_coste_papel) + floatval($presupuesto->total_coste_impresion); $sumForFactorPonderado = $sumForFactor + floatval($presupuesto->total_coste_servicios); @@ -2022,8 +2034,12 @@ class PresupuestoService extends BaseService $presupuesto->total_aceptado = $precio_total_asignado; $presupuesto->total_aceptado_revisado = $precio_total_asignado; $presupuesto->total_presupuesto = $precio_total_asignado; - $presupuesto->total_antes_descuento = $precio_total_asignado; + $presupuesto->total_antes_descuento = $precio_total_asignado - $costes - $envio_base < 0 ? + $costes + $envio_base : + $precio_total_asignado; $presupuesto->total_precio_unidad = $precio_unidad_asignado; + $presupuesto->total_descuento = $total_descuento; + $presupuesto->total_descuentoPercent = $total_descuentoPercent; $presupuesto->total_factor = round($factor, 2); $presupuesto->total_factor_ponderado = round($factorPonderado, 2); $model->update($id, $presupuesto); @@ -2035,4 +2051,27 @@ class PresupuestoService extends BaseService "new_precio_unidad" => $precio_unidad_asignado, ]; } + + public static function calcular_merma($tirada, $POD, $formas_lineas_interior = []) + { + + $merma = 0; + + if ($tirada > $POD) { + $merma = $tirada * 0.1; + } else { + $merma_lineas = []; + foreach ($formas_lineas_interior as $formas_linea) { + if ($formas_linea > $tirada) + array_push($merma_lineas, $formas_linea - $tirada); + else + array_push($merma_lineas, $tirada % $formas_linea); + } + if (count($merma_lineas) > 0) + $merma = max($merma_lineas); + } + + + return round($merma, 0); + } } diff --git a/httpdocs/assets/js/safekat/pages/importadores/catalogo/catalogo_tool.js b/httpdocs/assets/js/safekat/pages/importadores/catalogo/catalogo_tool.js index 7ed47bf2..5b9e19c8 100644 --- a/httpdocs/assets/js/safekat/pages/importadores/catalogo/catalogo_tool.js +++ b/httpdocs/assets/js/safekat/pages/importadores/catalogo/catalogo_tool.js @@ -175,8 +175,8 @@ document.addEventListener('DOMContentLoaded', function () { let icon = 'success'; if (result.price_warning) { - html = skUrl ? `La fila se importó exitosamente, pero el precio no se ha podido ajustar (debajo de costes).

Ver presupuesto` - : 'La fila se importó exitosamente, pero el precio no se ha podido ajustar (debajo de costes).'; + html = skUrl ? `La fila se importó exitosamente, pero el precio se ha ajustado debajo de costes.

Ver presupuesto` + : 'La fila se importó exitosamente, pero el precio se ha ajustado debajo de costes.'; icon = 'warning'; } @@ -305,7 +305,7 @@ document.addEventListener('DOMContentLoaded', function () { let text = 'Se han procesado todas las filas seleccionadas.'; let icon = 'success'; if (idsNoAjustados.length > 0) { - text = 'Se han procesado todas las filas seleccionadas, pero no se han podido ajustar los precios de los siguientes presupuestos: ' + idsNoAjustados.join(', '); + text = 'Se han procesado todas las filas seleccionadas, pero se han ajustado los siguientes presupuestos por debajo de costes: ' + idsNoAjustados.join(', '); icon = 'warning'; } Swal.fire({ diff --git a/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/lineasPresupuesto.js b/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/lineasPresupuesto.js index 6f991a0e..2944ff79 100644 --- a/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/lineasPresupuesto.js +++ b/httpdocs/assets/js/safekat/pages/presupuestoAdmin/sections/lineasPresupuesto.js @@ -1540,6 +1540,12 @@ class LineasPresupuesto { cliente_id: $('#clienteId').find(":selected").val(), }; + if($('#alert-datosLibro').html().includes(window.language.Presupuestos.validation.no_lp_for_merma) && + (uso == 'interior' || uso == 'interior_rot')){ + + datos.calcular_merma = 1; + } + if (datos.ancho == 0 || datos.alto == 0 || datos.ancho == '' || datos.alto == '' || isNaN(datos.ancho) || isNaN(datos.alto)) { return; }