diff --git a/ci4/app/Config/Routes/ImportadoresRoutes.php b/ci4/app/Config/Routes/ImportadoresRoutes.php index ca54d7aa..2706aa8c 100644 --- a/ci4/app/Config/Routes/ImportadoresRoutes.php +++ b/ci4/app/Config/Routes/ImportadoresRoutes.php @@ -6,20 +6,33 @@ use CodeIgniter\Router\RouteCollection; /* Rutas para tarifas */ $routes->group('importador', ['namespace' => 'App\Controllers\Importadores'], function ($routes) { - /* Libros */ + + /* Desde Catalogo */ $routes->group('catalogo', ['namespace' => 'App\Controllers\Importadores'], function ($routes) { /**====================== * Tool *========================**/ $routes->get('', 'ImportadorCatalogo::index', ['as' => 'importadorCatalogoTool']); - /**====================== * AJAX *========================**/ $routes->post('validar-fila', 'ImportadorCatalogo::validarFila'); $routes->post('importar-fila', 'ImportadorCatalogo::importarFila'); + }); + + /* Desde Cliente Bubok */ + $routes->group('bubok', ['namespace' => 'App\Controllers\Importadores'], function ($routes) { + /**====================== + * Tool + *========================**/ + $routes->get('', 'ImportadorBubok::index', ['as' => 'importadorBubokTool']); + + /**====================== + * AJAX + *========================**/ + $routes->post('importar-fila', 'ImportadorBubok::importarFila'); }); }); \ No newline at end of file diff --git a/ci4/app/Controllers/Importadores/ImportadorBubok.php b/ci4/app/Controllers/Importadores/ImportadorBubok.php new file mode 100644 index 00000000..1f2ff8f2 --- /dev/null +++ b/ci4/app/Controllers/Importadores/ImportadorBubok.php @@ -0,0 +1,363 @@ +viewData['pageTitle'] = lang('Importador.listingPage'); + $this->viewData['usingSweetAlert'] = true; + + // Breadcrumbs (IMN) + $this->viewData['breadcrumb'] = [ + ['title' => lang("App.menu_importadores"), 'route' => "javascript:void(0);", 'active' => false], + ['title' => lang("App.menu_importadores_bubok"), 'route' => route_to('importadorBubokTool'), 'active' => true] + ]; + + parent::initController($request, $response, $logger); + } + + + public function index() + { + + $viewData = [ + 'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Importador.importadorCatalogoTitle')]), + + ]; + + $viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class + + return view(static::$viewPath . 'viewImportadorBubokTool', $viewData); + } + + + public function importarFila() + { + $json = $this->request->getJSON(); + + // Validación mínima de datos comunes + $pedido = $json->pedido ?? null; + if (!$pedido || !isset($pedido->orderNumber)) { + return $this->respond([ + 'status' => 400, + 'message' => 'Datos comunes del pedido ausentes o inválidos.' + ]); + } + + // Validación mínima de existencia del producto en la linea + if (!$json || !isset($json->producto)) { + return $this->respond([ + 'status' => 400, + 'message' => 'Producto no proporcionado o inválido.' + ]); + } + $producto = $json->producto; + + // 1. Datos básicos: + // Referencia del cliente + $orderNumber = $pedido->orderNumber ?? null; + $productId = $producto->id ?? null; + if (is_null($orderNumber) || is_null($productId)) { + return $this->respond([ + 'status' => 400, + 'message' => 'Número de orden o ID del producto no reconocidos.' + ]); + } + $refCliente = "$orderNumber - $productId"; + + // Titulo + $titulo = $producto->title ?? null; + if (is_null($titulo)) { + return $this->respond([ + 'status' => 400, + 'message' => 'Título del libro no reconocido.' + ]); + } + + // Validación de páginas y tirada + $paginas = isset($producto->body->pages) ? (int) $producto->body->pages : 0; + $tirada = isset($producto->amount) ? (int) $producto->amount : 0; + if ($paginas <= 0 || $tirada <= 0) { + $errores = []; + if ($paginas <= 0) { + $errores[] = 'Número de páginas inválido.'; + } + if ($tirada <= 0) { + $errores[] = 'Tirada inválida.'; + } + + return $this->respond([ + 'status' => 400, + 'message' => implode(' ', $errores) + ]); + } + + // Ancho y alto + $ancho = null; + $alto = null; + foreach ($producto->size as $key => $val) { + if ($val == 1) { + // ejemplo: size170x235 + $size = str_replace('size', '', $key); + [$ancho, $alto] = explode('x', $size); + $ancho = (int) $ancho; + $alto = (int) $alto; + break; + } + } + if (!$ancho || !$alto) { + return $this->respond([ + 'status' => 400, + 'message' => 'Tamaño del libro no reconocido.' + ]); + } + + /*$numGuardaPages = 4; + $hasGuarda = !empty($producto->cover->guarda); + if ($hasGuarda) + $paginas += $numGuardaPages;*/ + + // 2. Interior: color o negro + // Determinar tipo de impresión interior + $interiorTipo = null; + if (isset($producto->body->color->CMYK) && $producto->body->color->CMYK == '1') { + $interiorTipo = 'color'; + } elseif (isset($producto->body->color->Monochrome) && $producto->body->color->Monochrome == '1') { + $interiorTipo = 'negro'; + } elseif (isset($producto->body->color->Semicolor) && $producto->body->color->Semicolor == '1') { + return $this->respond([ + 'status' => 400, + 'message' => 'Tipo de impresión "Semicolor" no soportado.' + ]); + } + + if (is_null($interiorTipo)) { + return $this->respond([ + 'status' => 400, + 'message' => 'No se pudo determinar si el interior es en color o blanco y negro.' + ]); + } + + // Determinar tipo de papel interior + $papelInteriorId = null; + if (isset($producto->body->paperColor->white) && $producto->body->paperColor->white == '1') { + $papelInteriorId = 3; // Offset blanco 'OFF1' + } elseif (isset($producto->body->paperColor->cream) && $producto->body->paperColor->cream == '1') { + $papelInteriorId = 4; // Offset ahuesado 'OFF2' + } else { + return $this->respond([ + 'status' => 400, + 'message' => 'Tipo de papel interior no definido.' + ]); + } + + // Determinar el gramaje del papel + $gramajePapelInterior = null; + foreach ($producto->body->paperWeight as $key => $val) { + if ($val == 1) { + $gramajePapelInterior = (int) str_replace(['weight', 'gr'], '', $key); + break; + } + } + if (!$gramajePapelInterior) { + return $this->respond([ + 'status' => 400, + 'message' => 'Gramaje del papel no válido.' + ]); + } + + // 3. Encuadernación + // Tapa dura + $tapaDura = isset($producto->cover->type->tapadura) && $producto->cover->type->tapadura == '1'; + + // Solapas + $solapas = isset($producto->cover->type->consolapas) && $producto->cover->type->consolapas == '1'; + + // Doble cara (a veces se activa con tapa dura) una cara => 2; dos caras => 4 + $doscara = false; + + // Tipo de encuadernado + $encuadernadoId = null; + + if (isset($producto->cover->coverType->SoftCover) && $producto->cover->coverType->SoftCover == '1') { + if ($tapaDura) { + $encuadernadoId = 1; // Libro fresado tapa dura + $doscara = true; + } else { + $encuadernadoId = 2; // Libro fresado tapa blanda + } + } elseif (isset($producto->cover->coverType->SaddleStitch) && $producto->cover->coverType->SaddleStitch == '1') { + if ($tapaDura) { + $encuadernadoId = 3; // Libro cosido tapa dura + $doscara = true; + } else { + $encuadernadoId = $solapas ? 20 : 4; // Libro cosido tapa blanda (solapas) : (sin solapas) + } + } elseif (isset($producto->cover->coverType->CoilBinding) && $producto->cover->coverType->CoilBinding == '1') { + if ($tapaDura) { + $encuadernadoId = 5; // Libro espiral tapa dura + $doscara = true; + } else { + $encuadernadoId = 6; // Libro espiral tapa blanda + } + } + if (!$encuadernadoId) { + return $this->respond([ + 'status' => 400, + 'message' => 'Tipo de encuadernación no identificado.' + ]); + } + + // Determinar el acabado de la cubierta + $acabadoId = null; + if (isset($producto->cover->acabado->brillo) && $producto->cover->acabado->brillo == '1') { + $acabadoId = 1; // Plastificado brillo 1/c + } elseif (isset($producto->cover->acabado->mate) && $producto->cover->acabado->mate == '1') { + $acabadoId = 2; // Plastificado mate 1/c + } else { + return $this->respond([ + 'status' => 400, + 'message' => 'Tipo de acabado de cubierta no definido.' + ]); + } + + // 4. ENVÍO: recuperamos la primera dirección del cliente BUBOK (ID 40) + $clienteDireccionModel = model('App\Models\Clientes\ClienteDireccionesModel'); + $direccionCliente = $clienteDireccionModel + ->where('cliente_id', 40) + ->orderBy('id', 'asc') + ->first(); + + if (!$direccionCliente) { + return $this->respond([ + 'status' => 400, + 'message' => 'El cliente Bubok no tiene direcciones asociadas.' + ]); + } + + $direcciones = [ + [ + 'direccion' => [ + 'id' => (int) $direccionCliente->id, + 'cliente_id' => (int) $direccionCliente->cliente_id, + 'cliente_nombre' => $direccionCliente->clienteNombre, + 'att' => $direccionCliente->persona_contacto ?? '', + 'alias' => $direccionCliente->alias ?? '', + 'email' => $direccionCliente->email ?? '', + 'direccion' => $direccionCliente->direccion, + 'pais_id' => (int) $direccionCliente->pais_id, + 'pais' => $direccionCliente->paisNombre, + 'municipio' => $direccionCliente->municipio, + 'provincia' => $direccionCliente->provincia, + 'cp' => $direccionCliente->cp, + 'telefono' => $direccionCliente->telefono, + ], + 'unidades' => $tirada, + 'entregaPalets' => false + ] + ]; + + // Generamos el objeto a importar + $dataToImport = [ + 'selectedTirada' => $tirada, + 'datosCabecera' => [ + 'titulo' => $titulo, + 'autor' => null, + 'isbn' => null, + 'coleccion' => null, + 'referenciaCliente' => $refCliente + ], + 'tirada' => [$tirada], + 'tamanio' => [ + 'ancho' => $ancho, + 'alto' => $alto + ], + 'tipo' => '', + 'tipo_presupuesto_id' => $encuadernadoId, + 'clienteId' => 40, // BUBOK ID + 'isColor' => ($interiorTipo === 'color') ? 1 : 0, + 'isHq' => 0, + 'paginas' => $paginas, + 'paginasColor' => ($interiorTipo === 'color') ? $paginas : 0, + 'paginasCuadernillo' => 32, + 'interior' => [ + 'papelInterior' => $papelInteriorId, + 'gramajeInterior' => $gramajePapelInterior + ], + 'cubierta' => [ + 'papelCubierta' => 2, // 'EST2' + 'carasCubierta' => $doscara ? 2 : 4, + 'gramajeCubierta' => in_array($encuadernadoId, [1, 3]) ? 150 : 300, // 150 gramos para "fresado tapa dura" y "cosido tapa dura" + 'solapas' => !empty($producto->cover->type->consolapas) ? 80 : 0, + 'acabado' => $acabadoId, + 'cabezada' => 'WHI', + 'lomoRedondo' => 0 + ], + 'guardas' => [], + 'sobrecubierta' => [], + 'faja' => null, + 'comentarios_safekat' => 'URL COVER: ' . $producto->cover->file . "\nURL BODY: " . $producto->body->file, + + 'direcciones' => $direcciones + ]; + + /*return $this->respond([ + 'status' => 400, + 'message' => $dataToImport + ]);*/ + + // 5. Guardar + try { + $presupuestocliente = new Presupuestocliente(); + $response = $presupuestocliente->guardar($dataToImport); + + if (!isset($response['sk_id'])) { + return $this->respond([ + 'status' => 400, + 'error' => 'Missing sk_id', + 'message' => 'No se pudo crear el presupuesto.' + ], 400); + } + + return $this->respond([ + 'status' => 200, + 'data' => [ + 'sk_id' => $response['sk_id'], + 'sk_url' => $response['sk_url'] ?? null + ] + ]); + + } catch (\Throwable $e) { + return $this->respond([ + 'status' => 500, + 'error' => 'Server error', + 'message' => 'Error inesperado', + 'debug' => $e->getMessage() + ]); + } + } + + + + +} diff --git a/ci4/app/Controllers/Importadores/ImportadorCatalogo.php b/ci4/app/Controllers/Importadores/ImportadorCatalogo.php index 30386acf..e2ebb080 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 { @@ -43,11 +44,8 @@ class ImportadorCatalogo extends BaseResourceController { $viewData = [ - 'currentModule' => static::$controllerSlug, 'pageSubTitle' => lang('Basic.global.ManageAllRecords', [lang('Importador.importadorCatalogoTitle')]), - 'catalogoLibrosEntity' => new CatalogoLibroEntity(), - 'usingServerSideDataTable' => true, - + ]; $viewData = array_merge($this->viewData, $viewData); // merge any possible values from the parent controller class @@ -241,9 +239,11 @@ class ImportadorCatalogo extends BaseResourceController 'lomoRedondo' => 0 ], + 'ivaReducido' => 1, + 'guardas' => [], 'sobrecubierta' => $sobrecubierta, - 'faja' => [], + //'faja' => null, 'entrega_taller' => 1, ]; @@ -281,17 +281,25 @@ 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, + null, + true + ); + if ($respuesta_ajuste['warning'] == true) { + $response['price_warning'] = [ + 'new_precio_unidad' => $respuesta_ajuste['new_precio_unidad'], + 'new_total' => $respuesta_ajuste['new_total'], + ]; + } + + // 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); 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 d6bce82f..6c0babdc 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -323,17 +323,23 @@ 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, '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); } } @@ -739,17 +751,23 @@ 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, '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); } @@ -1312,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; @@ -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]; @@ -2029,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, @@ -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); @@ -2107,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); } @@ -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) @@ -3142,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/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/ClienteDireccionesEntity.php b/ci4/app/Entities/Clientes/ClienteDireccionesEntity.php index 5cda2146..a96fe093 100755 --- a/ci4/app/Entities/Clientes/ClienteDireccionesEntity.php +++ b/ci4/app/Entities/Clientes/ClienteDireccionesEntity.php @@ -1,7 +1,9 @@ "int", "cp" => "int", ]; + + public function getClienteNombre(): ?string + { + if (!$this->cliente_id) { + return null; + } + + $clienteModel = model(ClienteModel::class); + $cliente = $clienteModel->find($this->cliente_id); + + return $cliente ? $cliente->nombre : null; + } + + public function getPaisNombre(): ?string + { + if (!$this->pais_id) { + return null; + } + + $paisModel = model(PaisModel::class); + $pais = $paisModel->find($this->pais_id); + + return $pais ? $pais->nombre : null; + } + } 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/App.php b/ci4/app/Language/es/App.php index a65094ca..417cb626 100755 --- a/ci4/app/Language/es/App.php +++ b/ci4/app/Language/es/App.php @@ -722,6 +722,7 @@ return [ "menu_importadores" => "Importadores", "menu_importadores_catalogo" => "Desde catálogo", + "menu_importadores_bubok" => "Bubok", "menu_catalogo" => "Catálogo", "menu_catalogo_libros" => "Libros", 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/Language/es/Importador.php b/ci4/app/Language/es/Importador.php index 8d4d36dd..925076fc 100644 --- a/ci4/app/Language/es/Importador.php +++ b/ci4/app/Language/es/Importador.php @@ -3,6 +3,7 @@ return [ 'moduleTitle' => 'Importadores', 'importadorCatalogoTitle' => 'Importador RA-MA Ediciones S.L.', + 'importadorBubokTitle' => 'Importador BUBOK Publishing S.L.', 'catalogo' => 'catálogo', 'input' => 'ISBN', 'descripcion' => 'Título', @@ -10,7 +11,8 @@ return [ 'cnt_pedida' => 'Unidades', 'precio_compra' => 'Precio Compra', 'importar' => 'Importar', - 'subirArchivo' => 'Cargar Excel proporcionado por RA-MA', + 'subirArchivoRama' => 'Cargar Excel proporcionado por RA-MA', + 'subirArchivoBubok' => 'Cargar XML proporcionado por BUBOK', 'libro' => 'libro', 'id' => 'ID', diff --git a/ci4/app/Language/es/RolesPermisos.php b/ci4/app/Language/es/RolesPermisos.php index ce946f1e..e9d23840 100755 --- a/ci4/app/Language/es/RolesPermisos.php +++ b/ci4/app/Language/es/RolesPermisos.php @@ -70,6 +70,7 @@ return [ 'catalogoSection' => 'Catálogo', 'importadoresSection' => 'Importadores', 'catalogoPermission' => 'Desde catálogo', + 'bubokPermission' => 'Bubok', 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..6feb5b4f 100755 --- a/ci4/app/Services/PresupuestoService.php +++ b/ci4/app/Services/PresupuestoService.php @@ -1983,4 +1983,95 @@ class PresupuestoService extends BaseService return $peso; } + + 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; + $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); + + $total_descuento = 0; + $total_descuentoPercent = 0; + if($costes + $envio_base > $precio_total_asignado){ + + 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 < 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); + + $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_presupuesto = $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); + } + return [ + "success" => true, + "warning" => $warning, + "new_total" => $precio_total_asignado, + "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/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 @@ +