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); } }