From a1aaa095d49466085111ef117eca4d45512600a4 Mon Sep 17 00:00:00 2001 From: Ignacio Martinez Navajas Date: Mon, 21 Jul 2025 21:55:27 +0200 Subject: [PATCH 1/6] Eliminado mecanismo de pasar a XML pedido --- ci4/app/Config/PedidoXML.php | 29 ---- ci4/app/Config/PresupuestoSFTP.php | 26 +++ ci4/app/Config/Routes.php | 1 - ci4/app/Controllers/Pedidos/Pedido.php | 8 +- ci4/app/Libraries/SafekatFtpClient.php | 32 +--- ci4/app/Services/PedidoXMLService.php | 222 ------------------------ ci4/app/Services/PresupuestoService.php | 1 - 7 files changed, 28 insertions(+), 291 deletions(-) delete mode 100755 ci4/app/Config/PedidoXML.php create mode 100644 ci4/app/Config/PresupuestoSFTP.php delete mode 100755 ci4/app/Services/PedidoXMLService.php 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..ec8db396 --- /dev/null +++ b/ci4/app/Config/PresupuestoSFTP.php @@ -0,0 +1,26 @@ +host = env("HIDRIVE_FILES_HOST","sftp.hidrive.ionos.com"); + $this->port = env("HIDRIVE_FILES_PORT",22); + $this->username = env("HIDRIVE_FILES_USER"); + $this->password = env("HIDRIVE_FILES_PASS"); + $this->base_dir = env("HIDRIVE_FILES_PATH_ROOT"); # FTP server directory + $this->id_offset = env("BUDGET_FILES_OFFSET_ID",1000000); + } +} 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/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/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/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) { From 9ed397e9ad87a6e8e5488d6e6ab202a5bc0e7d5b Mon Sep 17 00:00:00 2001 From: imnavajas Date: Tue, 22 Jul 2025 16:01:34 +0200 Subject: [PATCH 2/6] Desacople e inyeccion de dependencias --- .vscode/settings.json | 1 + ci4/app/Config/PresupuestoSFTP.php | 17 +- .../Presupuestos/Presupuestocliente.php | 159 ++++++++------ ci4/app/Controllers/Test.php | 14 +- ci4/app/Libraries/SftpClientWrapper.php | 43 ++++ .../Presupuestos/PresupuestoFicheroModel.php | 39 +++- .../Services/PresupuestoUploaderService.php | 76 +++++++ httpdocs/assets/img/pdf.png | Bin 0 -> 8183 bytes .../components/forms/fileUploadDropzone.js | 196 +++++++++++------- 9 files changed, 391 insertions(+), 154 deletions(-) create mode 100644 ci4/app/Libraries/SftpClientWrapper.php create mode 100644 ci4/app/Services/PresupuestoUploaderService.php create mode 100644 httpdocs/assets/img/pdf.png 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/PresupuestoSFTP.php b/ci4/app/Config/PresupuestoSFTP.php index ec8db396..a5dd8b1a 100644 --- a/ci4/app/Config/PresupuestoSFTP.php +++ b/ci4/app/Config/PresupuestoSFTP.php @@ -6,21 +6,22 @@ use CodeIgniter\Config\BaseConfig; class PresupuestoSFTP extends BaseConfig { - public string $host; public int $port; public string $username; public string $password; - public string $base_dir; # FTP server directory + public string $base_dir; public int $id_offset; - - public function __construct() { + + public function __construct() + { parent::__construct(); - $this->host = env("HIDRIVE_FILES_HOST","sftp.hidrive.ionos.com"); - $this->port = env("HIDRIVE_FILES_PORT",22); + $this->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->base_dir = env("HIDRIVE_FILES_PATH_ROOT"); # FTP server directory - $this->id_offset = env("BUDGET_FILES_OFFSET_ID",1000000); + $this->id_offset = (int) env("BUDGET_FILES_OFFSET_ID", 1000000); + $domain = parse_url(env("app.baseURL"), PHP_URL_HOST); + $this->base_dir = "/users/{$this->username}/{$domain}"; } } diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index 4d3de4c6..a6bd461e 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; @@ -1758,39 +1749,37 @@ 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); + // Aceptar solo POST (puedes cambiar a GET si lo necesitas) + if ($this->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() + + public function upload_files_old() { $model = model('App\Models\Presupuestos\PresupuestoFicheroModel'); @@ -1843,6 +1832,74 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return json_encode(['message' => 'Archivos subidos correctamente']); } + 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]; + } + + // Instanciar servicio con dependencias inyectadas manualmente + $service = new \App\Services\PresupuestoUploaderService( + new \App\Libraries\SftpClientWrapper(config('PresupuestoSFTP')), + $model, + config('PresupuestoSFTP') + ); + + // Borrar antiguos del SFTP y de la BD (pero no del disco local) + $service->removeFromRemote($presupuesto_id); + $model->deleteMissingFiles($presupuesto_id, $old_files); + + $results = []; + foreach ($files as $file) { + if (!$file->isValid()) { + $results[] = ['name' => $file->getName(), 'status' => 'invalid']; + continue; + } + + $newName = $model->saveFileInBBDD( + $presupuesto_id, + $file->getClientName(), + $file->getClientExtension(), + auth()->id() + ); + + $uploadPath = WRITEPATH . 'uploads/presupuestos/' . $newName; + $file->move(dirname($uploadPath), basename($uploadPath)); + + $results[] = ['name' => $file->getClientName(), 'status' => 'uploaded']; + } + + // Subida al SFTP + $sftpResult = $service->uploadToRemote($presupuesto_id); + + // Preparar notificación + if (!$sftpResult['success']) { + return $this->response->setJSON([ + 'message' => 'Error al subir uno o más archivos al SFTP.', + 'details' => [ + 'local' => $results, + 'sftp' => $sftpResult['files'] + ] + ])->setStatusCode(500); + } + + return $this->response->setJSON([ + 'message' => 'Archivos subidos correctamente al sistema y al SFTP.', + 'details' => [ + 'local' => $results, + 'sftp' => $sftpResult['files'] + ] + ]); + } + + + @@ -1936,6 +1993,7 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController { if ($tipo == 'encuadernacion') { + $model = new PresupuestoEncuadernacionesModel(); $data = [ @@ -2212,7 +2270,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } } @@ -2274,7 +2331,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } } } @@ -2345,7 +2401,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 +2441,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $margenServicios += round(floatval($acabadoCubierta[0]->total - $base), 2); } } - } if ($lomoRedondo) { @@ -2422,7 +2477,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 +2514,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalImpresion, $margenImpresion ); - } if ($coste_sobrecubierta <= 0) { @@ -2674,7 +2727,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 +2781,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 +2923,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 +2955,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 +3031,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController $totalServicios += $base; $margenServicios += round(floatval($resultado[0]->precio - $base), 2); } - } // Plegado de solapas grandes @@ -3222,7 +3270,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 +3389,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" @@ -3696,7 +3742,4 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController return $this->response->setStatusCode(500)->setBody('Error interno'); } } - - - } 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/Libraries/SftpClientWrapper.php b/ci4/app/Libraries/SftpClientWrapper.php new file mode 100644 index 00000000..42e12702 --- /dev/null +++ b/ci4/app/Libraries/SftpClientWrapper.php @@ -0,0 +1,43 @@ +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); + } + +} diff --git a/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php b/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php index 41deef1e..8ddae06d 100755 --- a/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php +++ b/ci4/app/Models/Presupuestos/PresupuestoFicheroModel.php @@ -32,7 +32,7 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel $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', 'uploads/presupuestos/' . $new_filename) ->set('upload_by', $user_id) ->set('upload_at', date('Y-m-d H:i:s')) ->insert(); @@ -54,8 +54,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 +77,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 +109,25 @@ 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); + + 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(); + } + } + } + + /** * Función para convertir el nombre y extensión de un fichero en un hash único @@ -117,6 +140,4 @@ class PresupuestoFicheroModel extends \App\Models\BaseModel { return hash('sha256', $filename); } - - } diff --git a/ci4/app/Services/PresupuestoUploaderService.php b/ci4/app/Services/PresupuestoUploaderService.php new file mode 100644 index 00000000..d8df6f21 --- /dev/null +++ b/ci4/app/Services/PresupuestoUploaderService.php @@ -0,0 +1,76 @@ +config->base_dir}/ficheros/" . ($presupuestoId + $this->config->id_offset); + + 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) { + $remotePath = $remoteDir . '/' . basename($file->file_path); + $localPath = WRITEPATH . $file->file_path; + $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 + ]; + } + + public function removeFromRemote(int $presupuestoId): void + { + $remoteDir = "{$this->config->base_dir}/pedidos_files/" . ($presupuestoId + $this->config->id_offset); + $files = $this->fileModel->getFiles($presupuestoId); + + foreach ($files as $file) { + $this->ftp->delete($remoteDir . '/' . basename($file->file_path)); + } + } + + public function removeMissingFromRemote(int $presupuestoId, array $keepFileNames): void + { + $remoteDir = "{$this->config->base_dir}/pedidos_files/" . ($presupuestoId + $this->config->id_offset); + $files = $this->fileModel->getFiles($presupuestoId); + + foreach ($files as $file) { + if (!in_array($file->nombre, $keepFileNames)) { + $this->ftp->delete($remoteDir . '/' . basename($file->file_path)); + } + } + } +} diff --git a/httpdocs/assets/img/pdf.png b/httpdocs/assets/img/pdf.png new file mode 100644 index 0000000000000000000000000000000000000000..5196ca4687953f37eacf7ccc2ab0aed13ed1ce13 GIT binary patch literal 8183 zcmd6Mdpy+JyZ@RoF}Z|PgwPe`nvh(}ElJpvg(?d%t5?{fzP1_sKwd3pFc+TXn+;vZN*mfT*8=&PDT}!Nrli&rkTH z>sIVK(LY*>el-4G8Ew5k6WMD2;8$DTV~>%KC2TLN?N5+MzI|YY_n`brT?G5Z{hVT_ zS>q%5BiVwa#C*bstE8e4>uLFT^R?KfI=JePx|G8bVUoahV|$~Kwup{5kUPu{e|?glKG4#h|7@|hW&N4c zg8aeeG$F4_Y3FO{r{Fx`Yw2v@No8;2vaAG8rjv)l!DbA2qXQD^k9BQxV2=)r#&1t9 zwgfmA)ih7Cx8GSC^k-vC&Gg%N?ni<$zZGNRklhOu|A`?7J{T{6zb@rxRJ5$8PKn~L zmd&IfBLNR6sNEnbZsjp$ud?U>BrZ&ffj9KRyhIJ9u>LW;oGVJ9xsv1fPgWKxcrA z2<`$vBs981*@b`|o;vi%1Xy79NMxmvH1F^u$D#Y##D-`^%U`r0NvmV{yPAe%bZT$Mq zjPnwtKCb!~dC$C~JGPEj?%A^o-|Xq?5;5DN{eOUpW92^pLtYBUE2}F17*LZuz%Gwn z3iw0n=;v4R(p$6;u0Nz&%6Z5BgK4fD_KJVUR5;;5d)j8#cR|=OEdZtrLu_#;qYw36 zd^S&*HZC~orJ9jNf#sd#sTZ_YWzh{wBzh(089_r$KmYLUG+ zt0&hr1SEE5Hu@%g@3-KV%-J85gBN#gk&Q1G4?wRMJ5(m@iNdi;nCD-Q`*PwI!6%Uo z2o$dgDK%;5WB6HE!XI@Zps=w%$xkY$UW7w$IX8W9`lbJevwQE4Kjm$Jlz>zYezx?9 zIkTc@!1?|Omk=AL4m2qkm@;~29$UK5_vxjRRV_x_{Kus3&X(6An4eA@blLMr|FNl5 zj+Nx&>~2v;#j9=VlrsE+8Yd`_RQImyK|9`Vs52?z68buI{%&`=s0a%#>}7X<0&0Zy zX}{Jv_>TMhY3u#(NPn|up}_MmJri20|Lnj2!lnMg%l=9Ft1tkW`k#P*3j_aSk{01O zBvEVM|Dpe{8yaAMPY4$KkC{R%x)T${pa%@>L`^NI1;v%WqCpFwrR0CEjk|K`ZT3h@ zdwom5&7#E@s8zjk$@Xn z*B)Z8`S|)29Z5i$sXKvz*q_YE?7gBRW0LSM)vcX_DoTVk?;~8zw}I1=Hm+rYDc;h4 zHd<3_)uWbFm`uit#;!*s3F$RCod$Y#o6JB#JrP0NAxqajwL@r^RFQ{k9$eZ06#(Vk z)!J_p++SYue!(ue9B)0@zypk*Va7V`vA9Etgd<T`>Vu+B5!|(Gmx_e&@vv&Y@GsxyAJL zVHYr=DHoBr!3sL8xyKY|Rj7=BnNw@Owex5d+U5Hyx4KOMj z$@KixXgZrb7J+}cOB;;em^mPhNd*6N83J<=~@5#RJw}`RfTEJs-&}-!Io9z6TH9a%<sN>%6k4Q=oF=hwwmB${}Zq4QB%7kCAD>+vg zry(=w+}`oSHJ?Su<7m8$#CNu(C~|?a-X?+Q9FSv0)$xuiOcE9x(XUov?{O~aASFsM zj%#jtabg$GnA+6#?R$b+EXz-*=Ccg^+MO3ME@_+SRqJ?lb1Es}dS3B6Y}^&HyX!_b zN7hF1nS>(_Dni9JziLxNm<(6qow%G=ZvK;7ClDCEY%A=MMmxs2ynIoQ(NgTpNW!NH zmeQHf{3}EeA;L1zVTz4d=14p4q3Me$#4;h@xi&v84U4x&i!(j^elT*SS83)V=o5Mb zv}ugBzpl4RPDq}!pgRhoPF_)FoPOMLrhS4-(>t%+mR-T6@p(^Y?C)Ra zDmGF?Ha=>ei0eJ8stk*d0?X34m@C{y<=qo@p+)U!eCpn`lw_a5qy&r5=u~u4qm8u;53tPtO$JrIie!C(JHr{^oa zewZ08rG5(?r4&j~*6(;64eKjjJq%RnHM{`Tl0P}%OD@&6E-|B^m%k30-oJ+~>a5oqfta`$dPfW~3IWBIEkLSj`$#hNU)j@DD8@8_sC z*hWhbLvxRcfw8E{eP|oM8j38J!|+U)4u!Drad75X@)-DSlT{mHpQco=;l)X6FqQ7$ zv9?SSJ@@Ktxtve!1`!(=W4(4H9Ktx4mg{#4#VJJW{qljz=)!i3uWpVvN8KoB`ZAFI zF13Oe0n~bbFX<3gk{Kfa&)yHTWr{38N+Q-X+&u#8WozvnH

$W5 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

- +
-
+
- - Descargar + + Descargar
`; + +// Clase principal que maneja el flujo de Dropzone + AJAX class FileUploadDropzone { - constructor({ domElement, nameId = "presupuesto_id", getUri = null, postUri = null, resourcePath = "presupuestos", otId = null }) { - Dropzone.autoDiscover = false; - this.domElement = domElement - this.jqElement = $(domElement) - this.modelId = this.jqElement.data('id') - this.btnSelectFiles = $(`#${domElement.replace('#', '')}_btnUploadFiles`) - this.btnSubmitFile = $(`#${domElement.replace('#', '')}_btnSubmitFiles`) + Dropzone.autoDiscover = false; // Desactiva la auto inicialización de Dropzone + + this.domElement = domElement; + this.jqElement = $(domElement); // Referencia jQuery al elemento + + this.modelId = this.jqElement.data('id'); // ID que asocia los archivos a un modelo (presupuesto, pedido, etc.) + + // Botones asociados + this.btnSelectFiles = $(`#${domElement.replace('#', '')}_btnUploadFiles`); + this.btnSubmitFile = $(`#${domElement.replace('#', '')}_btnSubmitFiles`); this.btnDownloadFiles = $(`#${domElement.replace('#', '')}_btnDownloadFiles`); - this.dataPost = {} + this.nameId = nameId; this.otId = otId; - this.getUri = getUri - this.postUri = postUri - this.dataPost[nameId] = this.modelId; - this.resourcePath = resourcePath + this.getUri = getUri; + this.postUri = postUri; + this.resourcePath = resourcePath; + this.dataPost = {}; + this.dataPost[nameId] = this.modelId; } + + // Inicializa Dropzone y los eventos externos init() { if (this.jqElement.length > 0) { - this.btnSubmitFile.on('click', this._handleUploadFiles.bind(this)) - this.btnSelectFiles.on('click', () => { - this.jqElement.trigger('click') - }) - this.btnDownloadFiles.on('click', this._handleDownloadFiles.bind(this)) + // Vincula botones externos + this.btnSubmitFile.on('click', this._handleUploadFiles.bind(this)); + this.btnSelectFiles.on('click', () => this.jqElement.trigger('click')); + this.btnDownloadFiles.on('click', this._handleDownloadFiles.bind(this)); + // Inicializa Dropzone this.dropzone = new Dropzone(this.domElement, { url: this.postUri, addRemoveLinks: true, previewTemplate: PREVIEW_TEMPLATE, paramName: "file", uploadMultiple: true, - parallelUploads: 4, // Ajusta este número al máximo número de archivos que esperas subir a la vez - maxFiles: 5, // Ajusta este número al máximo número de archivos que esperas subir a la vez + parallelUploads: 4, + maxFiles: 5, autoProcessQueue: true, dictRemoveFile: "Eliminar", acceptedFiles: 'image/*, application/pdf', - maxFilesize: 5e+7, // Bytes - init: this._handleGetFiles.bind(this) + maxFilesize: 5e+7, // 50 MB + init: this._handleGetFiles.bind(this) // Carga inicial de archivos }); + + // Cuando se añade un archivo (manual o programático) this.dropzone.on("addedfile", this._handleAddedFile.bind(this)); } } + // Botones "Ver" y "Descargar" para cada archivo _handleAddedFile(file) { if (file.hash) { // Botón Ver @@ -90,70 +101,86 @@ class FileUploadDropzone { } } + // Acción del botón "Ver" onViewButton(file) { - console.log(window.location.protocol + "//" + window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + file.hash) - window.open(window.location.protocol + "//" + window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + file.hash, '_blank'); + const url = `${window.location.protocol}//${window.location.host}/sistema/intranet/${this.resourcePath}/${file.hash}`; + window.open(url, '_blank'); } + + // Prepara el objeto FormData para el envío _getDropzoneFilesFormData() { - var files = this.dropzone.files; + const files = this.dropzone.files; + const formData = new FormData(); + const oldFiles = []; - var formData = new FormData(); - var oldFiles = []; - var counter = 0; - for (var i = 0; i < files.length; i++) { - - if (files[i].upload) { - var file = files[i]; + for (let file of files) { + if (file.upload) { formData.append('file[]', file); - counter += 1; - } - else { - oldFiles.push(files[i].name); + } else { + oldFiles.push(file.name); } } - formData.append('oldFiles', JSON.stringify(oldFiles)); + formData.append('oldFiles', JSON.stringify(oldFiles)); formData.append(this.nameId, this.modelId); + return formData; } + + // Acción al hacer clic en "Subir archivos" _handleUploadFiles() { - $("#loader").modal('show') - let ajax = new Ajax(this.postUri, + $("#loader").modal('show'); + const ajax = new Ajax( + this.postUri, this._getDropzoneFilesFormData(), null, this._handleUploadFilesSuccess.bind(this), - null) + null + ); ajax.ajaxForm("POST"); - } + + // Éxito tras subir archivos _handleUploadFilesSuccess(response) { - this.dropZoneClean() - this._handleGetFiles() + this.dropZoneClean(); // Limpia visualmente + this._handleGetFiles(); // Recarga archivos desde backend alertSuccessMessage(response?.message ?? "Archivos subidos correctamente"); } - _handleUploadFilesError(errors) { } + _handleUploadFilesError(errors) { + // No implementado aún + } + + // Carga inicial de archivos existentes desde el servidor _handleGetFiles() { const ajax = new Ajax( this.getUri, this.dataPost, null, this._handelGetFilesSuccess.bind(this), - null, - - ) - ajax.post() + null + ); + ajax.post(); } + + // Manejo de respuesta del servidor al cargar archivos _handelGetFilesSuccess(response) { try { - $("#loader").modal('hide') - const files = JSON.parse(response) - this.dropZoneUpdateFiles(files) + $("#loader").modal('hide'); + const files = Array.isArray(response) + ? response + : typeof response === 'string' + ? JSON.parse(response) + : []; + + this.dropZoneUpdateFiles(files); } catch (error) { - $("#loader").modal('hide') + console.error("Error parseando respuesta:", error); + $("#loader").modal('hide'); } } + // Manejo del botón "Descargar archivos ZIP" _handleDownloadFiles() { $("#loader").modal('show'); @@ -172,7 +199,7 @@ class FileUploadDropzone { let filename = "archivos.zip"; if (disposition && disposition.indexOf('attachment') !== -1) { const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition); - if (match != null && match[1]) { + if (match && match[1]) { filename = match[1].replace(/['"]/g, ''); } } @@ -195,28 +222,57 @@ class FileUploadDropzone { }); } - + // Carga archivos simulados (mock) al Dropzone visual dropZoneUpdateFiles(files) { - files.forEach(file => { - this.dropZoneAddFile(file) + console.log("Iterando archivo:", file.name); + this.dropZoneAddFile(file); }); - } + + // Limpia todos los archivos de Dropzone visualmente dropZoneClean() { this.dropzone.files.forEach(file => { this.dropzone.removeFile(file); - }) + }); } - dropZoneAddFile(mockFile) { - this.dropzone.files.push(mockFile); // add to files array - this.dropzone.emit("addedfile", mockFile); - this.dropzone.emit("thumbnail", mockFile, window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + mockFile.hash); - this.dropzone.emit("complete", mockFile); - this.dropzone.options.success.call(this.dropzone, mockFile); + // Inserta un archivo en Dropzone manualmente (mock) + dropZoneAddFile(mockFileData) { + const extension = mockFileData.name.split('.').pop().toLowerCase(); + const isImage = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(extension); + const isPDF = extension === 'pdf'; + + const fileUrl = `${window.location.protocol}//${window.location.host}/sistema/intranet/${this.resourcePath}/${mockFileData.hash}`; + + const mockFile = { + name: mockFileData.name, + size: mockFileData.size, + type: isImage ? `image/${extension === 'jpg' ? 'jpeg' : extension}` : 'application/pdf', + hash: mockFileData.hash, + upload: false // Impide que se vuelva a subir + }; + + this.dropzone.emit("addedfile", mockFile); + + // Espera a que Dropzone genere el DOM para modificar la miniatura + setTimeout(() => { + if (isImage) { + this.dropzone.emit("thumbnail", mockFile, fileUrl); + } else if (isPDF) { + const preview = mockFile.previewElement?.querySelector('.dz-thumbnail'); + if (preview) { + preview.innerHTML = `PDF`; + } + } + + this.dropzone.emit("complete", mockFile); + }, 10); + + this.dropzone.files.push(mockFile); } } -export default FileUploadDropzone; \ No newline at end of file +// Exporta la clase para usarla en otros módulos JS +export default FileUploadDropzone; From 065903be2fd2e957e592a29cbddda4afc2f16421 Mon Sep 17 00:00:00 2001 From: Ignacio Martinez Navajas Date: Tue, 22 Jul 2025 22:53:07 +0200 Subject: [PATCH 3/6] Cambio de naming en labels --- .../Presupuestos/Presupuestocliente.php | 52 ------------------- ci4/app/Language/es/App.php | 2 +- .../themes/vuexy/components/dropzone.php | 2 +- .../presupuestos/cliente/items/_resumen.php | 2 +- 4 files changed, 3 insertions(+), 55 deletions(-) diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php index a6bd461e..007759fe 100755 --- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php +++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php @@ -1779,58 +1779,6 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController } - public function upload_files_old() - { - - $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']); - } public function upload_files() { 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/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 @@