diff --git a/ci4/app/Config/Routes/PresupuestosRoutes.php b/ci4/app/Config/Routes/PresupuestosRoutes.php
index a4e9dd37..2f5ed704 100755
--- a/ci4/app/Config/Routes/PresupuestosRoutes.php
+++ b/ci4/app/Config/Routes/PresupuestosRoutes.php
@@ -30,6 +30,8 @@ $routes->group('presupuestoadmin', ['namespace' => 'App\Controllers\Presupuestos
$routes->get('presupuestosCliente', 'Presupuestoadmin::tablaClienteForm');
$routes->get('getSumCliente/(:num)', 'Presupuestoadmin::obtenerTotalPresupuestosCliente/$1');
+
+ $routes->post('download_zip', 'Presupuestocliente::download_zip', ['as' => 'descargarAdminArchivos']);
});
//$routes->resource('presupuestoadmin', ['namespace' => 'App\Controllers\Presupuestos', 'controller' => 'Presupuestoadmin', 'except' => 'show,new,create,update']);
@@ -51,6 +53,7 @@ $routes->group('presupuestocliente', ['namespace' => 'App\Controllers\Presupuest
$routes->post('calcular', 'Presupuestocliente::calcular', ['as' => 'calcularPresupuesto']);
$routes->post('calcularsolapas', 'Presupuestocliente::calcularMaxSolapas', ['as' => 'calcularSolapas']);
$routes->post('checklomo', 'Presupuestocliente::check_lomo_interior');
+ $routes->post('download_zip', 'Presupuestocliente::download_zip', ['as' => 'descargarClienteArchivos']);
});
//$routes->resource('presupuestocliente', ['namespace' => 'App\Controllers\Presupuestos', 'controller' => 'Presupuestocliente', 'except' => 'show,new,create,update']);
diff --git a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php
index 41395737..99f809a6 100755
--- a/ci4/app/Controllers/Presupuestos/Presupuestocliente.php
+++ b/ci4/app/Controllers/Presupuestos/Presupuestocliente.php
@@ -332,15 +332,14 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$cliente_model = model(('App\Models\Clientes\ClienteModel'));
$cliente = $cliente_model->find($cliente_id);
-
+
$forzarRotativa = false;
if ($tirada[0] <= $POD && $cliente->forzar_rotativa_pod) {
$forzarRotativa = true;
- }
- else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
+ } else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
$excluirRotativa = true;
- }
-
+ }
+
$input_data = array(
'uso' => 'interior',
@@ -760,14 +759,13 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$cliente_model = model(('App\Models\Clientes\ClienteModel'));
$cliente = $cliente_model->find($cliente_id);
-
+
$forzarRotativa = false;
if ($tirada[0] <= $POD && $cliente->forzar_rotativa_pod) {
$forzarRotativa = true;
- }
- else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
+ } else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
$excluirRotativa = true;
- }
+ }
$input_data = array(
'uso' => 'interior',
@@ -1340,8 +1338,8 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$datos_presupuesto['entrega_taller'] = $reqData['entrega_taller'] ?? 0;
- $resultado_presupuesto['info']['merma'] = isset($resultado_presupuesto['info']['num_formas']) ?
- PresupuestoService::calcular_merma($selected_tirada, $POD, $resultado_presupuesto['info']['num_formas']): PresupuestoService::calcular_merma($selected_tirada, $POD);
+ $resultado_presupuesto['info']['merma'] = isset($resultado_presupuesto['info']['num_formas']) ?
+ PresupuestoService::calcular_merma($selected_tirada, $POD, $resultado_presupuesto['info']['num_formas']) : PresupuestoService::calcular_merma($selected_tirada, $POD);
$datos_presupuesto['faja'] = $faja;
@@ -2080,14 +2078,13 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
$cliente_model = model(('App\Models\Clientes\ClienteModel'));
$cliente = $cliente_model->find($cliente_id);
-
+
$forzarRotativa = false;
if ($tirada[$t] <= $POD && $cliente->forzar_rotativa_pod) {
$forzarRotativa = true;
- }
- else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
+ } else if ($tirada[0] <= $POD && !$cliente->forzar_rotativa_pod) {
$excluirRotativa = true;
- }
+ }
$input_data = array(
'uso' => 'interior',
@@ -3586,4 +3583,29 @@ class Presupuestocliente extends \App\Controllers\BaseResourceController
return $servicios;
}
+ public function download_zip()
+ {
+ $presupuesto_id = $this->request->getPost('presupuesto_id');
+ if (!$presupuesto_id) {
+ return $this->response->setStatusCode(400)->setBody('Presupuesto ID requerido');
+ }
+
+ $ftpClient = new \App\Libraries\SafekatFtpClient();
+ try {
+ $zipPath = $ftpClient->downloadZipPresupuesto((int) $presupuesto_id);
+
+ if ($zipPath === null || !file_exists($zipPath)) {
+ return $this->response->setStatusCode(404)->setBody('No se encontraron archivos');
+ }
+
+ return $this->response
+ ->download($zipPath, null) // null = usar nombre original del archivo
+ ->setFileName('archivos_presupuesto_' . $presupuesto_id . '.zip');
+ } catch (\Throwable $e) {
+ log_message('error', $e->getMessage());
+ return $this->response->setStatusCode(500)->setBody('Error interno');
+ }
+ }
+
+
}
diff --git a/ci4/app/Language/es/App.php b/ci4/app/Language/es/App.php
index 66873e89..78ac6a61 100755
--- a/ci4/app/Language/es/App.php
+++ b/ci4/app/Language/es/App.php
@@ -35,6 +35,7 @@ return [
"global_next" => "Siguiente",
"global_save_file" => "Guardar ficheros",
"global_upload_files" => "Subir ficheros",
+ "global_download_files" => "Descargar ficheros",
"global_all" => "Todos",
// LOGIN - Index
"login_title" => "Iniciar sesión en su cuenta",
diff --git a/ci4/app/Libraries/SafekatFtpClient.php b/ci4/app/Libraries/SafekatFtpClient.php
index c69b2d85..9993eb49 100755
--- a/ci4/app/Libraries/SafekatFtpClient.php
+++ b/ci4/app/Libraries/SafekatFtpClient.php
@@ -114,4 +114,57 @@ class SafekatFtpClient
return implode('/', [$this->base_dir, 'pedidos_files', $rootIdExtern]);
}
+
+ public function downloadZipPresupuesto(int $presupuesto_id): ?string
+ {
+ $modelPedidoLinea = model(PedidoLineaModel::class);
+ $model = model(PresupuestoFicheroModel::class);
+
+ $pedidoLinea = $modelPedidoLinea->findByPresupuesto($presupuesto_id);
+ $rootIdExtern = $this->pedido_xml_config->id_offset + $pedidoLinea->pedido_id;
+
+ $remotePath = implode('/', [$this->base_dir, 'pedidos_files', $rootIdExtern]);
+
+ $this->ftp->login(username: $this->username, password: $this->password);
+
+ if (!$this->ftp->is_dir($remotePath)) {
+ return null;
+ }
+
+ $files = $model->getFiles($presupuesto_id);
+ if (empty($files)) {
+ return null;
+ }
+
+ $localTempDir = WRITEPATH . 'zip_presupuestos/' . uniqid("presupuesto_");
+ if (!is_dir($localTempDir)) {
+ mkdir($localTempDir, 0777, true);
+ }
+
+ foreach ($files as $file) {
+ $originalName = $file->nombre ?? basename($file->file_path);
+ $localFile = $localTempDir . '/' . $originalName;
+ $remoteFile = $remotePath . '/' . basename($file->file_path);
+ $this->ftp->get($remoteFile, $localFile);
+ }
+
+ $zipPath = $localTempDir . '.zip';
+ $zip = new \ZipArchive();
+ if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE)) {
+ foreach (glob($localTempDir . '/*') as $localFile) {
+ $zip->addFile($localFile, basename($localFile));
+ }
+ $zip->close();
+ }
+
+ // Limpieza temporal
+ foreach (glob($localTempDir . '/*') as $localFile) {
+ unlink($localFile);
+ }
+ rmdir($localTempDir);
+
+ return $zipPath;
+ }
+
+
}
diff --git a/ci4/app/Views/themes/vuexy/components/dropzone.php b/ci4/app/Views/themes/vuexy/components/dropzone.php
index ad2893df..4065ca91 100755
--- a/ci4/app/Views/themes/vuexy/components/dropzone.php
+++ b/ci4/app/Views/themes/vuexy/components/dropzone.php
@@ -31,6 +31,10 @@
= lang('App.global_save_file') ?>
+
diff --git a/ci4/app/Views/themes/vuexy/form/presupuestos/cliente/items/_resumen.php b/ci4/app/Views/themes/vuexy/form/presupuestos/cliente/items/_resumen.php
index 042ab029..fa41104b 100755
--- a/ci4/app/Views/themes/vuexy/form/presupuestos/cliente/items/_resumen.php
+++ b/ci4/app/Views/themes/vuexy/form/presupuestos/cliente/items/_resumen.php
@@ -213,6 +213,10 @@
= lang('App.global_save_file') ?>
+
diff --git a/httpdocs/assets/js/safekat/components/forms/fileUploadDropzone.js b/httpdocs/assets/js/safekat/components/forms/fileUploadDropzone.js
index a6e6fee8..4741f218 100644
--- a/httpdocs/assets/js/safekat/components/forms/fileUploadDropzone.js
+++ b/httpdocs/assets/js/safekat/components/forms/fileUploadDropzone.js
@@ -30,6 +30,7 @@ class FileUploadDropzone {
this.modelId = this.jqElement.data('id')
this.btnSelectFiles = $(`#${domElement.replace('#', '')}_btnUploadFiles`)
this.btnSubmitFile = $(`#${domElement.replace('#', '')}_btnSubmitFiles`)
+ this.btnDownloadFiles = $(`#${domElement.replace('#', '')}_btnDownloadFiles`);
this.dataPost = {}
this.nameId = nameId;
this.getUri = getUri
@@ -44,6 +45,7 @@ class FileUploadDropzone {
this.btnSelectFiles.on('click', () => {
this.jqElement.trigger('click')
})
+ this.btnDownloadFiles.on('click', this._handleDownloadFiles.bind(this))
this.dropzone = new Dropzone(this.domElement, {
url: this.postUri,
@@ -67,12 +69,12 @@ class FileUploadDropzone {
var viewButton = Dropzone.createElement("Ver");
file.previewElement.appendChild(viewButton);
// Listen to the view button click event
- viewButton.addEventListener("click", this.onViewButton.bind(this,file));
+ viewButton.addEventListener("click", this.onViewButton.bind(this, file));
}
}
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');
+ window.open(window.location.protocol + "//" + window.location.host + "/sistema/intranet/" + this.resourcePath + "/" + file.hash, '_blank');
}
_getDropzoneFilesFormData() {
var files = this.dropzone.files;
@@ -134,6 +136,47 @@ class FileUploadDropzone {
}
}
+ _handleDownloadFiles() {
+ $("#loader").modal('show');
+
+ $.ajax({
+ url: `/presupuestoadmin/download_zip`,
+ type: 'POST',
+ data: {
+ [this.nameId]: this.modelId
+ },
+ xhrFields: {
+ responseType: 'blob'
+ },
+ success: (blob, status, xhr) => {
+ const disposition = xhr.getResponseHeader('Content-Disposition');
+ let filename = "archivos.zip";
+ if (disposition && disposition.indexOf('attachment') !== -1) {
+ const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
+ if (match != null && match[1]) {
+ filename = match[1].replace(/['"]/g, '');
+ }
+ }
+
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ },
+ error: () => {
+ alertWarningMessage("Error al descargar el archivo ZIP.");
+ },
+ complete: () => {
+ $("#loader").modal('hide');
+ }
+ });
+ }
+
+
dropZoneUpdateFiles(files) {
files.forEach(file => {
diff --git a/httpdocs/assets/js/safekat/pages/presupuestoCliente/resumen.js b/httpdocs/assets/js/safekat/pages/presupuestoCliente/resumen.js
index 4471218c..031b08fb 100644
--- a/httpdocs/assets/js/safekat/pages/presupuestoCliente/resumen.js
+++ b/httpdocs/assets/js/safekat/pages/presupuestoCliente/resumen.js
@@ -58,6 +58,7 @@ class Resumen {
this.btnPreviewCubierta = $(this.domItem.find("#btnPreviewCubierta"));
this.btnDownloadPreviewCubierta = this.domItem.find('.download-shape');
this.submitFiles = $(this.domItem.find("#submit-all-files"));
+ this.btnDownloadAllFiles = this.domItem.find("#download-all-files");
this.dropzone = null;
this.presupuesto_id = -1;
@@ -68,6 +69,7 @@ class Resumen {
this.btnPreviewCubierta.on('click', this.#btnPreview.bind(this));
this.submitFiles.on('click', this.#btnUploadFiles.bind(this));
+ this.btnDownloadAllFiles.on('click', this.#downloadAllFiles.bind(this));
this.downloadPreviewImage()
if (presupuesto_id != -1) {
this.presupuesto_id = presupuesto_id;
@@ -186,6 +188,43 @@ class Resumen {
});
}
+ #downloadAllFiles() {
+ $('#loader').show();
+
+ $.ajax({
+ url: "/presupuestocliente/download_zip",
+ type: 'POST',
+ data: { presupuesto_id: this.presupuesto_id },
+ xhrFields: {
+ responseType: 'blob' // importante para recibir un archivo binario
+ },
+ success: function (blob, status, xhr) {
+ const disposition = xhr.getResponseHeader('Content-Disposition');
+ let filename = "archivos_presupuesto.zip";
+ if (disposition && disposition.indexOf('attachment') !== -1) {
+ const match = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(disposition);
+ if (match != null && match[1]) filename = match[1].replace(/['"]/g, '');
+ }
+
+ const url = window.URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ window.URL.revokeObjectURL(url);
+ },
+ error: function () {
+ popErrorAlert("Error al descargar el archivo ZIP");
+ },
+ complete: function () {
+ $('#loader').hide();
+ }
+ });
+ }
+
+
#btnPreview() {
@@ -384,7 +423,7 @@ class Resumen {
const svgData = serializer.serializeToString(shapeSvgEl);
const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
const svgUrl = URL.createObjectURL(svgBlob);
- const img = new Image(shapeSvgEl.getBoundingClientRect().width,shapeSvgEl.getBoundingClientRect().height);
+ const img = new Image(shapeSvgEl.getBoundingClientRect().width, shapeSvgEl.getBoundingClientRect().height);
img.onload = () => {
const scaleFactor = 3; // Increase resolution (e.g., 2x, 3x)
const canvas = document.createElement('canvas');
@@ -395,7 +434,7 @@ class Resumen {
canvas.height = height * scaleFactor;
const ctx = canvas.getContext('2d');
ctx.scale(scaleFactor, scaleFactor);
- ctx.drawImage(img, 0,0,width,height);
+ ctx.drawImage(img, 0, 0, width, height);
const pngUrl = canvas.toDataURL('image/png');
const downloadLink = document.createElement('a');
downloadLink.href = pngUrl;