diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a9917c8..ee2db733 100755 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -14,4 +14,5 @@ "username": "sk_imn" } ] + } \ No newline at end of file diff --git a/ci4/app/Config/Paths.php b/ci4/app/Config/Paths.php index d0035fbc..221145b2 100755 --- a/ci4/app/Config/Paths.php +++ b/ci4/app/Config/Paths.php @@ -72,4 +72,21 @@ class Paths * is used when no value is provided to `Services::renderer()`. */ public string $viewDirectory = __DIR__ . '/../Views'; + + /** + * Ruta base relativa dentro de WRITEPATH donde se almacenan + * los archivos asociados a presupuestos. + * + * Esta ruta se utiliza como base para componer las rutas + * completas tanto locales como remotas (SFTP) de ficheros + * relacionados con cada presupuesto. + * + * Ejemplo: + * Si el ID del presupuesto es 123 y el nombre del archivo es "documento.pdf", + * la ruta final será: storage/presupuestos/123/documento.pdf + * + * Se recomienda mantener esta ruta fuera de `public/` por razones de seguridad + * y utilizar controladores para servir los archivos si se desea acceso web. + */ + public string $presupuestosPath = 'storage/presupuestos'; } diff --git a/ci4/app/Config/PedidoXML.php b/ci4/app/Config/PedidoXML.php deleted file mode 100755 index 18d494ca..00000000 --- a/ci4/app/Config/PedidoXML.php +++ /dev/null @@ -1,29 +0,0 @@ -host = env("HIDRIVE_HOST","10.5.0.6"); - $this->port = env("HIDRIVE_PORT",21); - $this->username = env("HIDRIVE_USER","admin"); - $this->password = env("HIDRIVE_PASS","A77h3b0X4OA2rOYAf4w2"); - $this->base_dir = env("HIDRIVE_PATH_ROOT","/home/admin/safekat"); # FTP server directory - $this->xml_enabled = env("FTP_XML_ENABLED",false); - $this->id_offset = env("XML_OFFSET_CUSTOMER_ID",1000000); - - } -} diff --git a/ci4/app/Config/PresupuestoSFTP.php b/ci4/app/Config/PresupuestoSFTP.php new file mode 100644 index 00000000..4e1919ec --- /dev/null +++ b/ci4/app/Config/PresupuestoSFTP.php @@ -0,0 +1,38 @@ +host = env("HIDRIVE_FILES_HOST", "sftp.hidrive.ionos.com"); + $this->port = (int) env("HIDRIVE_FILES_PORT", 22); + $this->username = env("HIDRIVE_FILES_USER"); + $this->password = env("HIDRIVE_FILES_PASS"); + $this->id_offset = (int) env("BUDGET_FILES_OFFSET_ID", 1000000); + + // Directorio base remoto: /users/usuario/dominio + $domain = parse_url(env("app.baseURL"), PHP_URL_HOST); + $this->base_dir = "/users/{$this->username}/{$domain}"; + } + + /** + * Devuelve la ruta completa del directorio remoto para un presupuesto + */ + public function getRemoteDirForPresupuesto(int $presupuestoId): string + { + return "{$this->base_dir}/{$this->remote_base_dir}/" . ($presupuestoId + $this->id_offset); + } +} diff --git a/ci4/app/Config/Routes.php b/ci4/app/Config/Routes.php index bd702019..f6c490fc 100755 --- a/ci4/app/Config/Routes.php +++ b/ci4/app/Config/Routes.php @@ -500,7 +500,6 @@ $routes->group('pedidos', ['namespace' => 'App\Controllers\Pedidos'], function ( $routes->post('cambiarestado', 'Pedido::cambiarEstado', ['as' => 'cambiarEstadoPedido']); $routes->post('update/(:any)', 'Pedido::update/$1', ['as' => 'actualizarPedido']); $routes->post('insertfactura', 'Pedido::addFactura'); - $routes->get('xml/(:num)', 'Pedido::get_xml_pedido/$1', ['as' => 'getXMLPedido']); $routes->post('produccion/(:num)', 'Pedido::to_produccion/$1', ['as' => 'toProduccion']); $routes->get('pedidosCliente', 'Pedido::tablaClienteForm'); $routes->get('getSumCliente/(:num)', 'Pedido::obtenerTotalPedidosCliente/$1'); diff --git a/ci4/app/Config/Routes/PresupuestosRoutes.php b/ci4/app/Config/Routes/PresupuestosRoutes.php index 16929334..a4976457 100755 --- a/ci4/app/Config/Routes/PresupuestosRoutes.php +++ b/ci4/app/Config/Routes/PresupuestosRoutes.php @@ -69,4 +69,10 @@ $routes->group('importador', ['namespace' => 'App\Controllers\Presupuestos'], fu $routes->get('getencuadernacion', 'Importadorpresupuestos::getEncuadernacionList'); $routes->get('getpresupuestodata', 'Importadorpresupuestos::getPresupuesto', ['as' => 'getPresupuesto']); $routes->post('importar', 'Importadorpresupuestos::importarPresupuesto'); +}); + +$routes->group('files', ['namespace' => 'App\Controllers\Presupuestos'], function($routes) { + $routes->post('get_files', 'PresupuestoFicheroController::get_files', ['as' => 'getFiles']); + $routes->post('upload_files', 'PresupuestoFicheroController::upload_files', ['as' => 'uploadFiles']); + $routes->post('download_zip', 'PresupuestoFicheroController::download_zip', ['as' => 'downloadFilesZipped']); }); \ No newline at end of file diff --git a/ci4/app/Controllers/Importadores/ImportadorBubok.php b/ci4/app/Controllers/Importadores/ImportadorBubok.php index 03d46982..419223c3 100644 --- a/ci4/app/Controllers/Importadores/ImportadorBubok.php +++ b/ci4/app/Controllers/Importadores/ImportadorBubok.php @@ -1,4 +1,5 @@ $producto->cover->file ?? null, 'body' => $producto->body->file ?? null, ]; - foreach ($archivoUrls as $tipo => $url) { - if (!$url) - continue; + $resultadoArchivos = $uploaderService->importarArchivosDesdeUrlsBubok($response['sk_id'], $archivoUrls); - try { - $contenido = @file_get_contents($url); // silenciar errores de PHP - - if ($contenido === false || strlen($contenido) === 0) { - // No se pudo descargar el archivo: generar archivo de error para FTP - $errorMessage = "ERROR: No se pudo descargar el archivo remoto para $tipo desde la URL: $url"; - - $remoteDir = $ftp->getPresupuestoRemotePath($response['sk_id']); // crea esta función si no existe - $remoteErrorFile = $remoteDir . '/ERROR_' . strtoupper($tipo) . '.txt'; - - // Crear archivo temporal con el mensaje de error - $tempErrorFile = WRITEPATH . 'uploads/presupuestos/ERROR_' . $tipo . '.txt'; - file_put_contents($tempErrorFile, $errorMessage); - - if (!$ftp->is_dir($remoteDir)) { - $ftp->mkdir($remoteDir, recursive: true); - } - - $ftp->put($remoteErrorFile, $tempErrorFile, $ftp::SOURCE_LOCAL_FILE); - - continue; // no procesar este archivo - } - - // ✅ Procesar normalmente si la descarga tuvo éxito - $nombreOriginal = basename(parse_url($url, PHP_URL_PATH)); - $extension = pathinfo($nombreOriginal, PATHINFO_EXTENSION); - - $nombreLimpio = $presupuestoFicheroModel->saveFileInBBDD( - $response['sk_id'], - $nombreOriginal, - $extension, - auth()->id() - ); - - if (is_null($nombreLimpio)) - continue; - - $rutaLocal = WRITEPATH . 'uploads/presupuestos/'; - if (!is_dir($rutaLocal)) { - mkdir($rutaLocal, 0777, true); - } - - file_put_contents($rutaLocal . $nombreLimpio, $contenido); - } catch (\Throwable $e) { - //log_message('error', 'Error inesperado en descarga de archivo remoto: ' . $e->getMessage()); - } - } - - - // Subir al FTP después de guardar localmente - try { - $ftp->uploadFilePresupuesto($response['sk_id']); - } catch (\Throwable $e) { - log_message('error', 'Error subiendo archivos al FTP: ' . $e->getMessage()); + if (!$resultadoArchivos['success']) { + log_message('warning', 'Errores al importar archivos desde Bubok: ' . print_r($resultadoArchivos['errores'], true)); } return $this->respond([ 'status' => 200, 'data' => [ 'sk_id' => $response['sk_id'], - 'sk_url' => $response['sk_url'] ?? null + 'sk_url' => $response['sk_url'] ?? null, + 'archivos_subidos' => $resultadoArchivos['archivos_subidos'], + 'errores_archivos' => $resultadoArchivos['errores'] ] ]); - - } catch (\Throwable $e) { return $this->respond([ 'status' => 500, @@ -544,8 +493,4 @@ class ImportadorBubok extends BaseResourceController ]); } } - - - - } diff --git a/ci4/app/Controllers/Pedidos/Pedido.php b/ci4/app/Controllers/Pedidos/Pedido.php index c547754e..d05763e7 100755 --- a/ci4/app/Controllers/Pedidos/Pedido.php +++ b/ci4/app/Controllers/Pedidos/Pedido.php @@ -6,7 +6,6 @@ use App\Controllers\Facturacion\Facturas; use App\Entities\Pedidos\PedidoEntity; use App\Models\Collection; use App\Models\Pedidos\PedidoModel; -use App\Services\PedidoXMLService; use App\Services\ProductionService; use Hermawan\DataTables\DataTable; use CodeIgniter\I18n\Time; @@ -614,12 +613,7 @@ class Pedido extends \App\Controllers\BaseResourceController $pedidoEntity->created_at_footer = $pedidoEntity->created_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->created_at)) : ''; $pedidoEntity->updated_at_footer = $pedidoEntity->updated_at ? date(' H:i d/m/Y', strtotime($pedidoEntity->updated_at)) : ''; } - public function get_xml_pedido($pedido_id) - { - $data = PedidoXMLService::generate_xml($pedido_id); - // $xml_service = new PedidoXMLService($this->model); - return $this->respond($data); - } + public function to_produccion($pedido_id) { diff --git a/ci4/app/Controllers/Presupuestos/PresupuestoFicheroController.php b/ci4/app/Controllers/Presupuestos/PresupuestoFicheroController.php new file mode 100644 index 00000000..4b27c8c5 --- /dev/null +++ b/ci4/app/Controllers/Presupuestos/PresupuestoFicheroController.php @@ -0,0 +1,184 @@ +request->getMethod(true) !== 'POST') { + return $this->response->setStatusCode(405)->setJSON(['message' => 'Método no permitido']); + } + + $presupuesto_id = $this->request->getPost('presupuesto_id') ?? 0; + + $model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); + $files = $model->getFiles($presupuesto_id); + + $result = []; + + foreach ($files as $file) { + $relativePath = $file->file_path; + $fullPath = WRITEPATH . ltrim($relativePath, '/'); + + $relativePath = $file->file_path; + $basename = basename($relativePath); // solo el nombre del archivo + + $result[] = (object) [ + 'name' => $file->nombre, + 'size' => file_exists(WRITEPATH . $relativePath) ? filesize(WRITEPATH . $relativePath) : 0, + 'hash' => $basename + ]; + } + + return $this->response->setJSON($result); + } + + + public function upload_files() + { + $request = service('request'); + $model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); + $files = $request->getFiles()['file'] ?? []; + $presupuesto_id = $request->getPost('presupuesto_id'); + $old_files = json_decode($request->getPost('oldFiles') ?? '[]'); + + if (!is_array($files)) { + $files = [$files]; + } + + // Servicio de subida (con SFTP) + $service = new \App\Services\PresupuestoUploaderService( + new \App\Libraries\SftpClientWrapper(config('PresupuestoSFTP')), + $model, + config('PresupuestoSFTP') + ); + + // Borrar ficheros eliminados por el usuario (BD y remoto) + $service->removeFromRemote($presupuesto_id); + $numDeleted = $model->deleteMissingFiles($presupuesto_id, $old_files); + + $results = []; + foreach ($files as $file) { + if (!$file->isValid()) { + $results[] = [ + 'name' => $file->getName(), + 'status' => 'invalid', + 'message' => $file->getErrorString() + ]; + continue; + } + + $newName = $model->saveFileInBBDD( + $presupuesto_id, + $file->getClientName(), + $file->getClientExtension(), + auth()->id() + ); + + // Crear directorio si no existe + $uploadDir = dirname($model->getAbsolutePath($presupuesto_id, $newName)); + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } + + $file->move($uploadDir, $newName); + + $results[] = [ + 'name' => $file->getClientName(), + 'status' => 'uploaded' + ]; + } + + // Subida al SFTP + $sftpResult = $service->uploadToRemote($presupuesto_id); + + // ✅ Contar totales para mostrar en el frontend + $numUploaded = count(array_filter($results, fn($f) => $f['status'] === 'uploaded')); + $numErrores = count(array_filter($results, fn($f) => $f['status'] !== 'uploaded')); + + if (!$sftpResult['success']) { + return $this->response->setJSON([ + 'message' => 'Error en la subida de algunos archivos.', + 'summary' => [ + 'subidos_ok' => $numUploaded, + 'errores_locales' => $numErrores, + 'errores_remotos' => count(array_filter($sftpResult['files'], fn($f) => !$f['success'])), + 'borrados' => $numDeleted, + ], + 'details' => [ + 'local' => $results, + 'sftp' => $sftpResult['files'] + ] + ])->setStatusCode(500); + } + + return $this->response->setJSON([ + 'message' => 'Archivos subidos correctamente.', + 'summary' => [ + 'subidos_ok' => $numUploaded, + 'errores_locales' => $numErrores, + 'errores_remotos' => 0, + 'borrados' => $numDeleted + ], + 'details' => [ + 'local' => $results, + 'sftp' => $sftpResult['files'] + ] + ]); + } + + public function download_zip() + { + $presupuesto_id = $this->request->getPost('presupuesto_id'); + $ot_id = $this->request->getPost('ot_id'); + + if (!$presupuesto_id) { + return $this->response->setStatusCode(400)->setBody('Presupuesto ID requerido'); + } + + $prefijo = (!empty($ot_id) && is_numeric($ot_id)) ? "OT_{$ot_id}" : null; + + $service = new \App\Services\PresupuestoUploaderService( + new \App\Libraries\SftpClientWrapper(config('PresupuestoSFTP')), + model('App\Models\Presupuestos\PresupuestoFicheroModel'), + config('PresupuestoSFTP') + ); + + $result = $service->downloadZip((int) $presupuesto_id, $prefijo); + + if (!$result['success'] || empty($result['zipPath'])) { + return $this->response + ->setStatusCode(500) + ->setJSON(['error' => $result['message']]); + } + + $zipPath = $result['zipPath']; + + // Definir nombre final del ZIP para el cliente + $nombreArchivo = $prefijo + ? "{$prefijo}_PRESUPUESTO_{$presupuesto_id}.zip" + : "archivos_presupuesto_{$presupuesto_id}.zip"; + + // Eliminar archivo ZIP tras terminar la descarga (una vez enviada la respuesta) + register_shutdown_function(function () use ($zipPath) { + if (file_exists($zipPath)) { + unlink($zipPath); + } + }); + + // Descargar el archivo al cliente + return $this->response + ->download($zipPath, null) + ->setFileName($nombreArchivo); + } +} diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index 4d3de4c6..30cdb4f2 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -473,7 +473,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'errors' => $errors ); return $this->respond($data); - } else { return $this->failUnauthorized('Invalid request', 403); } @@ -615,7 +614,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return $this->failServerError( $return_data['exception'] . ' - ' . - $return_data['file'] . ' - ' . $return_data['line'] + $return_data['file'] . ' - ' . $return_data['line'] ); } @@ -733,7 +732,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } else { return $return_data; } - } catch (Exception $e) { if ($this->request) { if ($this->request->isAJAX()) @@ -742,7 +740,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return "Error: " . $e->getMessage(); } } - } @@ -849,11 +846,9 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $maxSolapa = (865 - floor($anchoTotal)) / 2; $maxSolapa = min($maxSolapa, 0.95 * $datosPedido->ancho); return $this->respond($maxSolapa); - } else { return $this->failUnauthorized('Invalid request', 403); } - } @@ -874,7 +869,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController 'menu' => $data, $csrfTokenName => $newTokenHash ]); - } else { return $this->failUnauthorized('Invalid request', 403); } @@ -1301,7 +1295,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $coste_envio += ($coste_direccion->coste / $tirada[$i]); $resultado_presupuesto['info']['totales'][$i]['coste_envio'] += $coste_direccion->coste - $coste_direccion->margen; $resultado_presupuesto['info']['totales'][$i]['margen_envio'] += $coste_direccion->margen; - } } $resultado_presupuesto['coste_envio'][$i] = round($coste_envio, 2); @@ -1336,7 +1329,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } else { $resumen_totales = $resultado_presupuesto['info']['totales'][$i]; $resumen_totales['precio_unidad'] = round($resultado_presupuesto['precio_u'][$i], 4); - } } @@ -1575,7 +1567,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController if (count($direccionesFP1) > 0) { $this->guardarLineaEnvio($id, $direccionesFP1, $peso_libro, true, true, 1); - } if (count($direccionesFP2) > 0) { $this->guardarLineaEnvio($id, $direccionesFP2, $peso_libro, true, true, 2); @@ -1732,7 +1723,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $data['resumen']['base'] = $presupuesto->total_antes_descuento; $data['resumen']['total_envio'] = round( floatval($presupuesto->total_coste_envios) + - floatval($presupuesto->total_margen_envios), + floatval($presupuesto->total_margen_envios), 2 ); $data['resumen']['precio_unidad'] = $presupuesto->total_precio_unidad; @@ -1756,96 +1747,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } } - public function get_files() - { - - // Check if the request is a POST request - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - - $presupuesto_id = $this->request->getPost()['presupuesto_id'] ?? 0; - - $model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); - $files = $model->getFiles($presupuesto_id); - - $result = []; - - foreach ($files as $file) { - - $size = filesize($file->file_path); - $splitPath = explode("presupuestos/", $file->file_path); - - // se crea un objeto con el nombre del fichero y el tamaño - $obj = (object) array( - 'name' => $file->nombre, - 'size' => $size, - 'hash' => $splitPath[1] ?? $file->file_path - ); - - - // se añade el objeto al array - array_push($result, $obj); - } - - return json_encode($result); - } - } - - public function upload_files() - { - - $model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - - $presupuesto_id = $_POST['presupuesto_id']; - $old_files = json_decode($_POST['oldFiles']); - $ftp = new SafekatFtpClient(); - - // Comprobar si se han subido archivos - if (!empty($_FILES['file']) || !empty($old_files)) { - - - // Borrar los archivos existentes del presupuesto - $ftp->removeFiles($presupuesto_id); - $model->deleteFiles($presupuesto_id, $old_files); - - if (!empty($_FILES['file'])) { - $files = $_FILES['file']; - - // Iterar sobre los archivos - for ($i = 0; $i < count($files['name']); $i++) { - // Aquí puedes acceder a las propiedades del archivo - $name = $files['name'][$i]; - $extension = explode('.', $files['name'][$i])[1]; - $tmp_name = $files['tmp_name'][$i]; - - $new_name = $model->saveFileInBBDD($presupuesto_id, $name, $extension, auth()->id()); - - // Se sube el fichero - // Pero primero se comprueba que la carpeta presupuestos exista - if (!is_dir(WRITEPATH . 'uploads/presupuestos')) { - mkdir(WRITEPATH . 'uploads/presupuestos', 0777, true); - } - - if (!is_null($new_name)) { - $path = WRITEPATH . 'uploads/presupuestos/' . $new_name; - move_uploaded_file($tmp_name, $path); - } - } - $ftp->uploadFilePresupuesto($presupuesto_id); - } - } else { - // Borrar los archivos existentes del presupuesto - $ftp->removeFiles($presupuesto_id); - $model->deleteFiles($presupuesto_id); - } - } - return json_encode(['message' => 'Archivos subidos correctamente']); - } - - - - + /*********************** * @@ -1936,6 +1838,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController { if ($tipo == 'encuadernacion') { + $model = new PresupuestoEncuadernacionesModel(); $data = [ @@ -2212,7 +2115,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } } @@ -2274,7 +2176,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } } } @@ -2345,7 +2246,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return $return_data; } - $cantidad_total = intval($datosPedido->tirada);// + intval($datosPedido->merma); + $cantidad_total = intval($datosPedido->tirada); // + intval($datosPedido->merma); // Acabado Cubierta if (intval($datos_entrada['cubierta']['acabado']) != 0) { @@ -2385,7 +2286,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $margenServicios += round(floatval($acabadoCubierta[0]->total - $base), 2); } } - } if ($lomoRedondo) { @@ -2422,7 +2322,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $totalServicios += $base; $margenServicios += round(floatval($resultado[0]->total - $base), 2); - } } @@ -2460,7 +2359,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } if ($coste_sobrecubierta <= 0) { @@ -2674,7 +2572,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $totalServicios += $base; $margenServicios += round(floatval($acabadoFaja[0]->total - $base), 2); - } } } @@ -2729,7 +2626,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $totalServicios += $base; $margenServicios += round(floatval($servicio->total - $base), 2); - } $servDefectoMan = PresupuestoCLienteService::getServiciosManipuladoDefault([ @@ -2872,7 +2768,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $totalServicios += $base; $margenServicios += round(floatval($resultado[0]->total - $base), 2); - } else if ($servicio->nombre == "ferro" || $servicio->nombre == "prototipo") { // Extra $resultado = PresupuestoCLienteService::getServiciosExtra([ @@ -2905,7 +2800,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $base = round(floatval($base / $cantidad_total), 2) * $cantidad_total; $totalServicios += $base; $margenServicios += round(floatval($resultado[0]->total - $base), 2); - } else if ($servicio->nombre == 'solapas_cubierta' || $servicio->nombre == 'solapas_sobrecubierta' || $servicio->nombre == 'solapas_faja') { // Servicios manipulado $resultado = PresupuestoCLienteService::getServiciosManipulado([ @@ -2982,7 +2876,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalServicios += $base; $margenServicios += round(floatval($resultado[0]->precio - $base), 2); } - } // Plegado de solapas grandes @@ -3222,7 +3115,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $margenImpresion += round($linea['margen_impresion_horas'], 2); $margenImpresion += round($linea['margen_click_pedido'], 2); $margenImpresion = round($margenImpresion, 2); - } protected function calcular_lomo($lineas, $lomo_inicial) @@ -3342,8 +3234,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $color = 'negro'; $model = model('App\Models\Presupuestos\PresupuestoLineaModel'); - $data = $model->where('presupuesto_id', $presupuestoId)->findAll(); - ; + $data = $model->where('presupuesto_id', $presupuestoId)->findAll();; foreach ($data as $linea) { if (strpos($linea->tipo, "hq") !== false) { // $linea->tipo contains the substring "hq" @@ -3663,40 +3554,5 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return $servicios; } - - public function download_zip() - { - $presupuesto_id = $this->request->getPost('presupuesto_id'); - $ot_id = $this->request->getPost('ot_id'); - - if (!$presupuesto_id) { - return $this->response->setStatusCode(400)->setBody('Presupuesto ID requerido'); - } - - // Definir prefijo si se recibió un ot_id válido - $prefijo = (!empty($ot_id) && is_numeric($ot_id)) ? "OT_{$ot_id}" : null; - - $ftpClient = new \App\Libraries\SafekatFtpClient(); - try { - $zipPath = $ftpClient->downloadZipPresupuesto((int) $presupuesto_id, $prefijo); - - if ($zipPath === null || !file_exists($zipPath)) { - return $this->response->setStatusCode(404)->setBody('No se encontraron archivos'); - } - - $nombreArchivo = $prefijo - ? "{$prefijo}_PRESUPUESTO_{$presupuesto_id}.zip" - : "archivos_presupuesto_{$presupuesto_id}.zip"; - - return $this->response - ->download($zipPath, null) - ->setFileName($nombreArchivo); - } catch (\Throwable $e) { - log_message('error', $e->getMessage()); - return $this->response->setStatusCode(500)->setBody('Error interno'); - } - } - - - + } diff --git a/ci4/app/Controllers/Sistema/Intranet.php b/ci4/app/Controllers/Sistema/Intranet.php index c01d9b96..aa3141d4 100755 --- a/ci4/app/Controllers/Sistema/Intranet.php +++ b/ci4/app/Controllers/Sistema/Intranet.php @@ -3,6 +3,7 @@ namespace App\Controllers\Sistema; use CodeIgniter\Controller; +use App\Models\Presupuestos\PresupuestoFicheroModel; class Intranet extends Controller { @@ -11,25 +12,24 @@ class Intranet extends Controller { helper('file'); - $resource_path = WRITEPATH . 'uploads/presupuestos/' . $resource_name; + $model = new PresupuestoFicheroModel(); + $file = $model->where('file_path LIKE', "%{$resource_name}")->first(); - if (file_exists($resource_path)) { - // Get the mime type of the file - $mime_type = mime_content_type($resource_path); - - // Get an instance of the Response class - $response = service('response'); - - // Set the content type - $response->setContentType($mime_type); - - // Set the output - $response->setBody(file_get_contents($resource_path)); - - // Send the response to the browser - $response->send(); + if (!$file) { + return service('response')->setStatusCode(404)->setBody("Archivo no encontrado"); } + $resource_path = WRITEPATH . $file->file_path; + + if (file_exists($resource_path)) { + $mime_type = mime_content_type($resource_path); + $response = service('response'); + $response->setContentType($mime_type); + $response->setBody(file_get_contents($resource_path)); + $response->send(); + } else { + return service('response')->setStatusCode(404)->setBody("Archivo no encontrado"); + } } function tickets($resource_name) @@ -54,7 +54,6 @@ class Intranet extends Controller // Send the response to the browser $response->send(); } - } function orden_trabajo($ot_id, $resource_name) { @@ -76,7 +75,6 @@ class Intranet extends Controller // Send the response to the browser $response->send(); } - } function catalogo($catalogo_id, $resource_name) @@ -99,7 +97,5 @@ class Intranet extends Controller // Send the response to the browser $response->send(); } - } - -} \ No newline at end of file +} diff --git a/ci4/app/Controllers/Test.php b/ci4/app/Controllers/Test.php index 345a47e9..10618d27 100755 --- a/ci4/app/Controllers/Test.php +++ b/ci4/app/Controllers/Test.php @@ -15,25 +15,24 @@ use App\Models\Catalogo\CatalogoLibroModel; use App\Services\PresupuestoService; use CodeIgniter\Shield\Entities\User; +use App\Libraries\SftpClientWrapper; +use Config\PresupuestoSFTP; + class Test extends BaseController { - function __construct() - { - } + function __construct() {} public function echo() { echo "echo"; - } public function index() { - - + } @@ -75,10 +74,8 @@ class Test extends BaseController // Insert it $tel_model->insert($tarifasLinea); - } } - } @@ -224,7 +221,6 @@ class Test extends BaseController } else { $values = []; } - } diff --git a/ci4/app/Language/es/App.php b/ci4/app/Language/es/App.php index e501c8e6..2956eee3 100755 --- a/ci4/app/Language/es/App.php +++ b/ci4/app/Language/es/App.php @@ -34,7 +34,7 @@ return [ "global_prev" => "Anterior", "global_next" => "Siguiente", "global_save_file" => "Guardar ficheros", - "global_upload_files" => "Subir ficheros", + "global_select_files" => "Seleccionar ficheros", "global_download_files" => "Descargar ficheros", "global_all" => "Todos", // LOGIN - Index diff --git a/ci4/app/Libraries/SafekatFtpClient.php b/ci4/app/Libraries/SafekatFtpClient.php index 25d7a597..1421c38a 100755 --- a/ci4/app/Libraries/SafekatFtpClient.php +++ b/ci4/app/Libraries/SafekatFtpClient.php @@ -15,52 +15,23 @@ class SafekatFtpClient protected string $username; protected string $password; protected string $base_dir; - protected bool $xml_enabled; protected object $pedido_xml_config; public function __construct() { - $this->pedido_xml_config = config("PedidoXML"); + $this->pedido_xml_config = config("PresupuestoSFTP"); $this->host = $this->pedido_xml_config->host; $this->username = $this->pedido_xml_config->username; $this->password = $this->pedido_xml_config->password; $this->port = $this->pedido_xml_config->port; $this->base_dir = $this->pedido_xml_config->base_dir; - $this->xml_enabled = $this->pedido_xml_config->xml_enabled; $this->ftp = new SFTP($this->host); } - /** - * Upload the content of $filename to the base directory declared in App\Config\FTP.php - * - * @param string $content - * @param string $filename - * @return boolean - */ - public function uploadXML(string $content, string $filename): bool - { - try { - if ($this->xml_enabled == false) - return false; - $remotePath = implode("/", [$this->base_dir, 'pedidos', 'xml_nuevos']); - $this->ftp->login(username: $this->username, password: $this->password); - if (!$this->ftp->is_dir($remotePath)) { - $this->ftp->mkdir($remotePath, recursive: true); - } - $this->ftp->put($remotePath . '/' . $filename, $content); - return true; - } catch (\Throwable $th) { - throw $th; - log_message('error', $th->getMessage()); - return false; - } - } public function uploadFilePresupuesto(int $presupuesto_id) { try { - if ($this->xml_enabled == false) - return false; $model = model(PresupuestoFicheroModel::class); $modelPedidoLinea = model(PedidoLineaModel::class); $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id); @@ -86,7 +57,6 @@ class SafekatFtpClient public function removeFiles(int $presupuesto_id) { try { - // if ($this->xml_enabled == false) return false; $model = model(PresupuestoFicheroModel::class); $modelPedidoLinea = model(PedidoLineaModel::class); $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id); diff --git a/ci4/app/Libraries/SftpClientWrapper.php b/ci4/app/Libraries/SftpClientWrapper.php new file mode 100644 index 00000000..96897451 --- /dev/null +++ b/ci4/app/Libraries/SftpClientWrapper.php @@ -0,0 +1,47 @@ +client = new SFTP($config->host, $config->port); + $this->client->login($config->username, $config->password); + } + + public function upload(string $local, string $remote): bool + { + return $this->client->put($remote, $local, SFTP::SOURCE_LOCAL_FILE); + } + + public function delete(string $remote): bool + { + return $this->client->delete($remote); + } + + public function exists(string $remote): bool + { + return $this->client->file_exists($remote); + } + + public function mkdir(string $remote): bool + { + return $this->client->mkdir($remote, true); + } + + public function chmod(string $path, int $permissions): bool + { + return $this->client->chmod($permissions, $path); + } + + public function get(string $remotePath, string $localPath): bool + { + return $this->client->get($remotePath, $localPath); + } +} diff --git a/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php b/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php index 41deef1e..9b8f6a2b 100755 --- a/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php +++ b/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php @@ -2,6 +2,8 @@ namespace App\Models\Presupuestos; +use Config\Paths; + class PresupuestoFicheroModel extends \App\Models\BaseModel { protected $table = "presupuesto_ficheros"; @@ -23,16 +25,42 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel public static $labelField = "nombre"; + /** + * Devuelve la ruta relativa del archivo dentro de WRITEPATH. + * + * @param int $presupuesto_id + * @param string $filename + * @return string + */ + public function getRelativePath(int $presupuesto_id, string $filename): string + { + return config(Paths::class)->presupuestosPath . '/' . $presupuesto_id . '/' . $filename; + } + + /** + * Devuelve la ruta absoluta en el sistema de archivos del servidor. + * + * @param int $presupuesto_id + * @param string $filename + * @return string + */ + public function getAbsolutePath(int $presupuesto_id, string $filename): string + { + return WRITEPATH . $this->getRelativePath($presupuesto_id, $filename); + } + public function saveFileInBBDD($presupuesto_id, $filename, $extension, $user_id) { try { + $new_filename = $this->generateFileHash($filename) . '.' . $extension; + $relativePath = $this->getRelativePath($presupuesto_id, $new_filename); $this->db->table($this->table . " t1") ->set('presupuesto_id', $presupuesto_id) ->set('nombre', $filename) - ->set('file_path', WRITEPATH . 'uploads/presupuestos/' . $new_filename) + ->set('file_path', $relativePath) ->set('upload_by', $user_id) ->set('upload_at', date('Y-m-d H:i:s')) ->insert(); @@ -54,8 +82,9 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel // se comprueba que el $file->nombre no sea igual a ninguno de los elementos del array $old_files if (!in_array($file->nombre, $old_files)) { - if (file_exists($file->file_path)) { - unlink($file->file_path); + $fullPath = WRITEPATH . $file->file_path; + if (file_exists($fullPath)) { + unlink($fullPath); } $this->db @@ -76,20 +105,23 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel ->table($this->table . " t1") ->where('presupuesto_id', $presupuesto_id_origen)->get()->getResult(); if ($files) { - + foreach ($files as $file) { $hash = $this->generateFileHash($file->nombre); // se copia el fichero a la nueva ubicación - if (!file_exists(WRITEPATH . $file->file_path)) { - copy($file->file_path, WRITEPATH . 'uploads/presupuestos/' . $hash); + $originalPath = WRITEPATH . $file->file_path; + $newPath = 'uploads/presupuestos/' . $hash; + + if (file_exists($originalPath)) { + copy($originalPath, WRITEPATH . $newPath); } $this->db->table($this->table . " t1") ->set('presupuesto_id', $presupuesto_id_destino) ->set('nombre', $file->nombre) - ->set('file_path', WRITEPATH . 'uploads/presupuestos/' . $hash) + ->set('file_path', $newPath) ->set('upload_by', auth()->user()->id) ->set('upload_at', date('Y-m-d H:i:s')) ->insert(); @@ -105,6 +137,30 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel ->where('presupuesto_id', $presupuesto_id)->get()->getResult(); } + public function deleteMissingFiles(int $presupuesto_id, array $keepNames = []) + { + $files = $this->getFiles($presupuesto_id); + $deletedCount = 0; + + foreach ($files as $file) { + if (!in_array($file->nombre, $keepNames)) { + $fullPath = WRITEPATH . $file->file_path; + if (file_exists($fullPath)) { + unlink($fullPath); + } + $this->db->table($this->table) + ->where('presupuesto_id', $presupuesto_id) + ->where('nombre', $file->nombre) + ->delete(); + + $deletedCount++; + } + } + + return $deletedCount; + } + + /** * Función para convertir el nombre y extensión de un fichero en un hash único @@ -117,6 +173,4 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel { return hash('sha256', $filename); } - - } diff --git a/ci4/app/Services/PedidoXMLService.php b/ci4/app/Services/PedidoXMLService.php deleted file mode 100755 index 103aa41d..00000000 --- a/ci4/app/Services/PedidoXMLService.php +++ /dev/null @@ -1,222 +0,0 @@ -getPedidoClientePresupuesto($pedido_id); - $data_xml['pedido_presupuesto_direcciones'] = $pedidoModel->getPedidoPresupuestoDirecciones($pedido_id); - $data_xml['pedido_presupuesto_lineas'] = $pedidoModel->getPedidoPresupuestoLineas($pedido_id); - $servicios = $presupuestoModel->getServiciosPresupuesto($data_xml['pedido_cliente_presupuesto']->presupuestoId); - $data_xml['servicios'] = $servicios; - $data_xml['preimpresion'] = PedidoXMLService::parse_servicio_preimpresion($servicios['preimpresion']); - $data_xml["acabado"] = PedidoXMLService::parse_servicio_acabado($servicios['acabado']); - $data_xml["binding"] = PedidoXMLService::get_binding_code($data_xml['pedido_cliente_presupuesto']->codigoTipoImpresion,$data_xml['pedido_cliente_presupuesto']->solapas); - return $data_xml; - - } - protected static function parse_servicio_acabado(array $data_xml_servicios_acabado) - { - $xml_element = []; - $service_xml_key_value = [ - "ShrinkWrapping" => fn($nombre) => str_contains($nombre,"retractilado"), - "Finish" => fn($nombre) => str_contains($nombre,"brillo"), - "PlakeneT" =>fn($nombre) => str_contains($nombre,"plakene traslúcido"),]; - foreach($data_xml_servicios_acabado as $servicio_acabado) - { - $service_name = strtolower($servicio_acabado->nombre); - foreach ($service_xml_key_value as $key => $value) { - $xml_element[$key] = $value($service_name) ? 1 : 0 ; - } - - } - return $xml_element; - } - protected static function parse_servicio_preimpresion(array $data_xml_servicios_preimpresion) - { - $xml_element = []; - $service_xml_key_value = [ - "Urgent" => fn($nombre) => str_contains($nombre,"Pedido urgente"), - "Prototype" => fn($nombre) => str_contains($nombre,"Prototipo"), - "Layout" =>fn($nombre) => str_contains($nombre,"Maquetación"), - "Correction" =>fn($nombre) => str_contains($nombre,"Corrección ortográfica"), - // "Review" =>fn($nombre) => str_contains($nombre,"Revisión Profesional de archivo"), - "Design" =>fn($nombre) => str_contains($nombre,'Diseño de Cubierta'), - ]; - foreach($data_xml_servicios_preimpresion as $servicio_pre) - { - $service_name = $servicio_pre->nombre; - foreach ($service_xml_key_value as $key => $value) { - $value_service = $value($service_name) ? 1 : 0 ; - if( $value_service){ - $xml_element[$key] = $servicio_pre->precio ; - }else if(!isset($xml_element[$key])){ - $xml_element[$key] = $value_service; - } - } - } - return $xml_element; - } - public static function generate_xml($pedido_id) - { - $papel_formato_ancho = 0; - $papel_formato_alto = 0; - $data = PedidoXMLService::get_pedido_presupuesto($pedido_id); - $xml = new DOMDocument('1.0', 'utf-8'); - $xml_order_el = $xml->createElement('Order'); - $xml_header_el = $xml->createElement('Header'); - $offset_pedido_id = env('XML_OFFSET_CUSTOMER_ID',1000000) + $data["pedido_cliente_presupuesto"]->pedidoId; - $xml_header_el->appendChild($xml->createElement('CustomerCode', $data["pedido_cliente_presupuesto"]->presupuestoClienteId)); - $xml_header_el->appendChild($xml->createElement('CodeNode', env('NODE_CODE_XML','SFK'))); - $xml_header_el->appendChild($xml->createElement('ExternId', $offset_pedido_id)); - $xml_header_el->appendChild($xml->createElement('NumProducts', 1)); - $xml_header_el->appendChild($xml->createElement('Date', now_db())); - $xml_order_el->appendChild($xml_header_el); - $xml_products_el = $xml->createElement('Products'); - $xml_product_el = $xml->createElement('Product'); - $xml_product_el->appendChild($xml->createElement('ItemId', $offset_pedido_id)); - $xml_product_el->appendChild($xml->createElement('Quantity', $data["pedido_cliente_presupuesto"]->tirada)); - $xml_product_el->appendChild($xml->createElement('Title', $data["pedido_cliente_presupuesto"]->titulo)); - $xml_product_el->appendChild($xml->createElement('Pages', $data["pedido_cliente_presupuesto"]->paginas)); - $xml_product_el->appendChild($xml->createElement('Reprint', $data["pedido_cliente_presupuesto"]->inc_rei ?? 0)); - - if ($data["pedido_cliente_presupuesto"]->papel_formato_personalizado) { - $papel_formato_ancho = $data["pedido_cliente_presupuesto"]->papelAnchoPersonalidado; - $papel_formato_alto = $data["pedido_cliente_presupuesto"]->papelAltoPersonalidado; - } else { - $papel_formato_ancho = $data["pedido_cliente_presupuesto"]->lgPapelFormatoAncho; - $papel_formato_alto = $data["pedido_cliente_presupuesto"]->lgPapelFormatoAlto; - } - $xml_product_el->appendChild($xml->createElement('Width', $papel_formato_ancho)); - $xml_product_el->appendChild($xml->createElement('Height', $papel_formato_alto)); - $presupuestoLineaTipoCubierta = null; - $xml_presupuesto_lineas_el = $xml->createElement('Lines'); - ## Iterate throught presupuesto_lineas - foreach ($data["pedido_presupuesto_lineas"] as $row) { - - if (str_contains($row->tipo, "rot") || str_contains($row->tipo, "bn") || str_contains($row->tipo, "color")) { - $colorInterior = PedidoXMLService::get_color_interior($row); - $xmlInside = $xml->createElement('Inside'); - $xmlInside->appendChild($xml->createElement('TypeOfPrint', $colorInterior)); - $xmlInside->appendChild($xml->createElement('HQ', str_contains($row->tipo, 'hq') ? 1 : 0)); - $xmlInside->appendChild($xml->createElement('Pages', $row->paginas)); - $xmlInside->appendChild($xml->createElement('Paper', $row->papelCode)); - $xmlInside->appendChild($xml->createElement('Weight', $row->gramaje)); - $xml_presupuesto_lineas_el->appendChild($xmlInside); - } else if (str_contains($row->tipo, "lp_cubierta") ) {//|| str_contains($row->tipo, "sobrecubierta") - //? If both exists presupuestoLineaTipoCubierta is override by sobreCubierta making null and not adding - $papelCubiertaCode = $row->papelCode; - $papelCubiertaGramaje = $row->gramaje; - $presupuestoLineaTipoCubierta = $row->tipo == "lp_cubierta" ? $row : null; - } - } - $xml_product_el->appendChild($xml_presupuesto_lineas_el); - if ($presupuestoLineaTipoCubierta) { - $containsTarifaAcabadoBrillo = isset($data['acabado']['Finish']) ? true : false; - if ($containsTarifaAcabadoBrillo) { - $acabado = "brillo"; - } else { - $acabado = "mate"; - } - $xmlCover = $xml->createElement('Cover'); - $xmlCover->appendChild($xml->createElement('Sides', $presupuestoLineaTipoCubierta->paginas / 2)); - $xmlCover->appendChild($xml->createElement('Paper', $presupuestoLineaTipoCubierta->papelCode)); - $xmlCover->appendChild($xml->createElement('Weight', $presupuestoLineaTipoCubierta->gramaje)); - $xmlCover->appendChild($xml->createElement('Flaps', $data["pedido_cliente_presupuesto"]->solapas)); - $xmlCover->appendChild($xml->createElement('WidthFlaps', $data["pedido_cliente_presupuesto"]->solapas_ancho)); - $xmlCover->appendChild($xml->createElement('Finish', $acabado)); - $xml_product_el->appendChild($xmlCover); - } - $xml_product_el->appendChild($xml->createElement('Binding', $data['binding'])); - $xml_services_el = $xml->createElement('Services'); - $xml_services_el->appendChild($xml->createElement('Bookmark', $data["pedido_cliente_presupuesto"]->marcapaginas)); - foreach ($data['preimpresion'] as $key => $value) { - $xml_services_el->appendChild($xml->createElement($key, $value)); - } - foreach ($data['acabado'] as $key => $value) { - $xml_services_el->appendChild($xml->createElement($key, $value)); - } - - $xml_product_el->appendChild($xml_services_el); - - $xml_envios_el = $xml->createElement('Shipments'); - foreach ($data["pedido_presupuesto_direcciones"] as $pedido_presupuesto_direccion) { - $xml_envio_el = $xml->createElement('Shipment'); - $xml_envio_el->appendChild($xml->createElement('Qty', $pedido_presupuesto_direccion->cantidad)); - $xml_envio_el->appendChild($xml->createElement('Price', $pedido_presupuesto_direccion->precio)); - $xml_envio_el->appendChild($xml->createElement('Attention', $pedido_presupuesto_direccion->att)); - $xml_envio_el->appendChild($xml->createElement('Email', $pedido_presupuesto_direccion->email)); - $xml_envio_el->appendChild($xml->createElement('Address', $pedido_presupuesto_direccion->direccion)); - $xml_envio_el->appendChild($xml->createElement('Province', $pedido_presupuesto_direccion->provincia)); - $xml_envio_el->appendChild($xml->createElement('City', $pedido_presupuesto_direccion->municipio)); - $xml_envio_el->appendChild($xml->createElement('Zip', $pedido_presupuesto_direccion->cp)); - $xml_envio_el->appendChild($xml->createElement('CountryCode', $pedido_presupuesto_direccion->paisCode3)); - $xml_envio_el->appendChild($xml->createElement('Telephone', $pedido_presupuesto_direccion->telefono)); - $xml_envios_el->appendChild($xml_envio_el); - } - $xml_product_el->appendChild($xml_envios_el); - $xml_product_el->appendChild($xml->createElement('Comments', $data["pedido_cliente_presupuesto"]->comentarios_safekat)); - $xml_product_el->appendChild($xml->createElement('CommentsClient', $data["pedido_cliente_presupuesto"]->comentarios_cliente)); - $xml_products_el->appendChild($xml_product_el); - $xml_order_el->appendChild($xml_products_el); - $xml->appendChild($xml_order_el); - $file_has_suffix = hash('sha512',$offset_pedido_id); - $file_name = PedidoXMLService::generate_xml_file_name($file_has_suffix); - $ftp = new SafekatFtpClient(); - $ftp->uploadXML($xml->saveXML(),$file_name); - return $data; - } - protected static function generate_xml_file_name(string $hash) : string - { - return implode("",["SafekatNew_",$hash,".xml"]); - } - protected static function get_binding_code(string $tipo_impresion_nombre,bool $solapas) : ?string - { - $solapa = $solapas ? '1' : '0'; - $key = implode("_",[$tipo_impresion_nombre,$solapa]); - $xml_mapping_binding = - [ - "libroFresadoTapaBlanda_0" => 'RF', - "libroFresadoTapaBlanda_1" => 'RFS', - "libroCosidoTapaBlanda_0" => 'RCHV', - "libroCosidoTapaBlanda_1" => 'RCHVS', - "libroGrapado_0" => 'CC2', - "libroGrapado_1" => 'CC2S', - "libroCosidoTapaDura_0" => 'TDC', - "libroCosidoTapaDura_1" => 'TDC', - "libroFresadoTapaDura_0" => 'RDF', - "libroFresadoTapaDura_1" => 'RDF', - "libroEspiralTapaBlanda_0" => 'ESP', - "libroEspiralTapaBlanda_1" => 'ESP', - "libroWireoTapaBlanda_0" => 'WIO', - "libroWireoTapaBlanda_1" => 'WIO', - ]; - return $xml_mapping_binding[$key] ?? null; - } - protected static function get_color_interior($pre_linea): ?string - { - $color_interior = null; - $bn_tipo_array = ['lp_bn', 'lp_bnhq', 'lp_rot_bn']; - $color_tipo_array = ['lp_color', 'lp_color_hq', 'lp_rot_color']; - - if (in_array($pre_linea->tipo, $bn_tipo_array)) { - $color_interior = "bn"; - }; - if (in_array($pre_linea->tipo, $color_tipo_array)) { - $color_interior = "color"; - }; - return $color_interior; - } -} diff --git a/ci4/app/Services/PresupuestoService.php b/ci4/app/Services/PresupuestoService.php index 54cd2aa5..59e632d9 100755 --- a/ci4/app/Services/PresupuestoService.php +++ b/ci4/app/Services/PresupuestoService.php @@ -1903,7 +1903,6 @@ class PresupuestoService extends BaseService "user_updated_id" => auth()->user()->id, ]; $id_linea = $model_pedido_linea->insert($data_pedido_linea); - //PedidoXMLService::generate_xml($pedido_id); } if ($id_linea != 0 && $pedido_id != 0) { diff --git a/ci4/app/Services/PresupuestoUploaderService.php b/ci4/app/Services/PresupuestoUploaderService.php new file mode 100644 index 00000000..19174fd3 --- /dev/null +++ b/ci4/app/Services/PresupuestoUploaderService.php @@ -0,0 +1,306 @@ +config->getRemoteDirForPresupuesto($presupuestoId); + + if (!$this->ftp->exists($remoteDir)) { + if (!$this->ftp->mkdir($remoteDir, true)) { + return [ + 'success' => false, + 'message' => "No se pudo crear el directorio remoto: $remoteDir" + ]; + } + $this->ftp->chmod($remoteDir, 0755); + } + + $files = $this->fileModel->getFiles($presupuestoId); + $results = []; + + foreach ($files as $file) { + $filename = basename($file->file_path); + $localPath = WRITEPATH . $file->file_path; + $remotePath = $remoteDir . '/' . $filename; + + if (!file_exists($localPath)) { + $results[] = [ + 'file' => $file->nombre, + 'remotePath' => $remotePath, + 'success' => false, + 'error' => 'Archivo local no encontrado' + ]; + continue; + } + + $ok = $this->ftp->upload($localPath, $remotePath); + $results[] = [ + 'file' => $file->nombre, + 'remotePath' => $remotePath, + 'success' => $ok + ]; + } + + $allOk = !in_array(false, array_column($results, 'success')); + + return [ + 'success' => $allOk, + 'files' => $results + ]; + } + + /** + * Elimina todos los archivos actuales del presupuesto del SFTP. + */ + public function removeFromRemote(int $presupuestoId): array + { + $remoteDir = $this->config->getRemoteDirForPresupuesto($presupuestoId); + $files = $this->fileModel->getFiles($presupuestoId); + + $results = []; + + foreach ($files as $file) { + $filename = basename($file->file_path); + $remotePath = $remoteDir . '/' . $filename; + + if ($this->ftp->exists($remotePath)) { + $deleted = $this->ftp->delete($remotePath); + $results[] = [ + 'file' => $file->nombre, + 'remotePath' => $remotePath, + 'success' => $deleted, + 'message' => $deleted ? 'Eliminado correctamente' : 'Falló al eliminar' + ]; + } else { + $results[] = [ + 'file' => $file->nombre, + 'remotePath' => $remotePath, + 'success' => false, + 'message' => 'Archivo no encontrado en el SFTP' + ]; + } + } + + return $results; + } + + /** + * Elimina del SFTP los archivos que ya no existen en la lista permitida. + */ + public function removeMissingFromRemote(int $presupuestoId, array $keepFileNames): void + { + $remoteDir = $this->config->getRemoteDirForPresupuesto($presupuestoId); + $files = $this->fileModel->getFiles($presupuestoId); + + foreach ($files as $file) { + if (!in_array($file->nombre, $keepFileNames)) { + $remotePath = $remoteDir . '/' . basename($file->file_path); + if ($this->ftp->exists($remotePath)) { + $this->ftp->delete($remotePath); + } + } + } + } + + + /** + * Descarga archivos de SFTP y genera un ZIP temporal para el presupuesto dado. + * + * @param int $presupuestoId ID del presupuesto. + * @param string|null $prefijo Prefijo para los nombres de los archivos. + * @return array Estructura: ['success' => bool, 'message' => string, 'zipPath' => string|null] + */ + public function downloadZip(int $presupuestoId, ?string $prefijo = null): array + { + $files = $this->fileModel->getFiles($presupuestoId); + if (empty($files)) { + return [ + 'success' => false, + 'message' => "No hay archivos en la base de datos para el presupuesto ID {$presupuestoId}.", + 'zipPath' => null + ]; + } + + $remoteDir = $this->config->getRemoteDirForPresupuesto($presupuestoId); + if (!$this->ftp->exists($remoteDir)) { + return [ + 'success' => false, + 'message' => "El directorio remoto no existe: {$remoteDir}", + 'zipPath' => null + ]; + } + + $localTempDir = WRITEPATH . 'zip_presupuestos/' . uniqid("presupuesto_"); + if (!is_dir($localTempDir) && !mkdir($localTempDir, 0777, true)) { + return [ + 'success' => false, + 'message' => "No se pudo crear el directorio temporal en: {$localTempDir}", + 'zipPath' => null + ]; + } + + $erroresDescarga = []; + foreach ($files as $file) { + $originalName = $file->nombre ?? basename($file->file_path ?? ''); + $prefixedName = $prefijo ? $prefijo . '_' . $originalName : $originalName; + + $localFile = $localTempDir . '/' . $prefixedName; + $remoteFile = $remoteDir . '/' . basename($file->file_path ?? ''); + + if (!$this->ftp->get($remoteFile, $localFile)) { + $erroresDescarga[] = "Error al descargar: {$remoteFile}"; + } + } + + if (count($erroresDescarga) === count($files)) { + return [ + 'success' => false, + 'message' => "Fallo al descargar todos los archivos:\n" . implode("\n", $erroresDescarga), + 'zipPath' => null + ]; + } + + $zipPath = $localTempDir . '.zip'; + $zip = new \ZipArchive(); + if (!$zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) { + return [ + 'success' => false, + 'message' => "No se pudo crear el archivo ZIP: {$zipPath}", + 'zipPath' => null + ]; + } + + foreach (glob($localTempDir . '/*') as $localFile) { + $zip->addFile($localFile, basename($localFile)); + } + $zip->close(); + + foreach (glob($localTempDir . '/*') as $localFile) { + unlink($localFile); + } + rmdir($localTempDir); + + if (!file_exists($zipPath)) { + return [ + 'success' => false, + 'message' => "El ZIP no fue generado correctamente.", + 'zipPath' => null + ]; + } + + return [ + 'success' => true, + 'message' => "ZIP generado correctamente.", + 'zipPath' => $zipPath + ]; + } + + + public function importarArchivosDesdeUrlsBubok(int $presupuestoId, array $urls): array + { + $resultados = []; + $errores = []; + + foreach ($urls as $tipo => $url) { + if (empty($url)) { + $errores[] = "URL vacía para tipo: {$tipo}"; + continue; + } + + try { + // Intenta descargar el contenido del archivo desde la URL + $contenido = @file_get_contents($url); + + // Si no se puede descargar, se genera un archivo de error local (no se guarda en BBDD) + if ($contenido === false || strlen($contenido) === 0) { + $mensajeError = "ERROR: No se pudo descargar el archivo remoto para $tipo desde la URL: $url"; + $nombreError = 'ERROR_' . strtoupper($tipo) . '.txt'; + + // Ruta local en la estructura estándar (no BBDD) + $rutaError = $this->fileModel->getAbsolutePath($presupuestoId, $nombreError); + + // Crear el directorio si no existe + $directorio = dirname($rutaError); + if (!is_dir($directorio)) { + mkdir($directorio, 0755, true); + } + + // Guardar el archivo con el mensaje de error + file_put_contents($rutaError, $mensajeError); + + // Registrar el error en el array de errores + $errores[] = $mensajeError; + continue; + } + + // Nombre original y extensión del archivo descargado + $nombreOriginal = basename(parse_url($url, PHP_URL_PATH)); + $extension = pathinfo($nombreOriginal, PATHINFO_EXTENSION); + + // Registrar el archivo en la base de datos y obtener el nombre limpio + $nombreLimpio = $this->fileModel->saveFileInBBDD( + $presupuestoId, + $nombreOriginal, + $extension, + auth()->id() + ); + + if (!$nombreLimpio) { + $errores[] = "No se pudo registrar '$nombreOriginal' en la base de datos."; + continue; + } + + // Obtener la ruta completa donde se guardará el archivo localmente + $rutaAbsoluta = $this->fileModel->getAbsolutePath($presupuestoId, $nombreLimpio); + + // Crear el directorio si no existe + $directorio = dirname($rutaAbsoluta); + if (!is_dir($directorio)) { + mkdir($directorio, 0755, true); + } + + // Guardar el archivo en el sistema de archivos local + file_put_contents($rutaAbsoluta, $contenido); + + // Añadir el archivo a la lista de resultados + $resultados[] = [ + 'nombre' => $nombreLimpio, + 'tipo' => $tipo, + 'ruta_local' => $rutaAbsoluta + ]; + } catch (\Throwable $e) { + $errores[] = "Error inesperado procesando '$tipo': " . $e->getMessage(); + } + } + + // Subida de todos los archivos válidos al servidor remoto SFTP + try { + $this->uploadToRemote($presupuestoId); + } catch (\Throwable $e) { + $errores[] = "Error al subir archivos al SFTP: " . $e->getMessage(); + } + + // Devolver el resumen del proceso + return [ + 'success' => empty($errores), + 'archivos_subidos' => $resultados, + 'errores' => $errores + ]; + } +} diff --git a/ci4/app/Views/themes/vuexy/components/dropzone.php b/ci4/app/Views/themes/vuexy/components/dropzone.php index 3ca591e9..139289a1 100755 --- a/ci4/app/Views/themes/vuexy/components/dropzone.php +++ b/ci4/app/Views/themes/vuexy/components/dropzone.php @@ -24,7 +24,7 @@