0; $cliente = null; $errors = []; $success = ''; // Labels for tramites $tipoLabels = [ 'visa' => 'Visa', 'sentri' => 'Sentri/Global', 'pasaporte' => 'Pasaporte', 'adelanto_cita' => 'Adelanto Cita', 'doble_nacionalidad' => 'Doble Nacionalidad', ]; $estadoLabels = [ 'nuevo' => 'Nuevo', 'en_proceso' => 'En Proceso', 'en_revision' => 'En Revisión', 'completado' => 'Completado', 'cancelado' => 'Cancelado', ]; // ── Handle POST actions ───────────────────────────────────────── if ($_SERVER['REQUEST_METHOD'] === 'POST' && csrfValidate()) { $action = $_POST['action'] ?? 'save_client'; switch ($action) { // ── Save client (create / update) ───────────────────────── case 'save_client': $nombre = trim($_POST['nombre'] ?? ''); $telefono = trim($_POST['telefono'] ?? ''); $email = trim($_POST['email'] ?? ''); $direccion = trim($_POST['direccion'] ?? ''); $notas = trim($_POST['notas'] ?? ''); if ($nombre === '') { $errors[] = 'El nombre es obligatorio.'; } if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) { $errors[] = 'El email no es válido.'; } if (empty($errors)) { if ($isEdit) { $stmt = $db->prepare("UPDATE clientes SET nombre=?, telefono=?, email=?, direccion=?, notas=? WHERE id=?"); $stmt->execute([$nombre, $telefono, $email, $direccion, $notas, $clienteId]); header("Location: cliente-detalle.php?id=$clienteId&saved=1"); exit; } else { $stmt = $db->prepare("INSERT INTO clientes (nombre, telefono, email, direccion, notas) VALUES (?,?,?,?,?)"); $stmt->execute([$nombre, $telefono, $email, $direccion, $notas]); $newId = $db->lastInsertId(); // If created from solicitud, link them $fromSolicitud = (int)($_POST['from_solicitud'] ?? 0); if ($fromSolicitud > 0) { $stmt2 = $db->prepare("UPDATE solicitudes SET cliente_id=?, estado='convertida' WHERE id=?"); $stmt2->execute([$newId, $fromSolicitud]); } header("Location: cliente-detalle.php?id=$newId&saved=1"); exit; } } break; // ── Add credential ──────────────────────────────────────── case 'add_credential': if (!$isEdit) break; $portal = trim($_POST['cred_portal'] ?? ''); $usuario = trim($_POST['cred_usuario'] ?? ''); $password = $_POST['cred_password'] ?? ''; $credNotas = trim($_POST['cred_notas'] ?? ''); if ($portal === '' || $usuario === '' || $password === '') { $errors[] = 'Portal, usuario y contraseña son obligatorios.'; } else { $passwordEnc = encryptData($password); $stmt = $db->prepare("INSERT INTO credenciales (cliente_id, portal, usuario, password_enc, notas) VALUES (?,?,?,?,?)"); $stmt->execute([$clienteId, $portal, $usuario, $passwordEnc, $credNotas]); header("Location: cliente-detalle.php?id=$clienteId&cred_saved=1#credenciales"); exit; } break; // ── Delete credential ───────────────────────────────────── case 'delete_credential': if (!$isEdit) break; $credId = (int)($_POST['cred_id'] ?? 0); if ($credId > 0) { $stmt = $db->prepare("DELETE FROM credenciales WHERE id=? AND cliente_id=?"); $stmt->execute([$credId, $clienteId]); } header("Location: cliente-detalle.php?id=$clienteId&cred_deleted=1#credenciales"); exit; // ── Upload document ─────────────────────────────────────── case 'upload_document': if (!$isEdit) break; $docNombre = trim($_POST['doc_nombre'] ?? ''); $file = $_FILES['doc_archivo'] ?? null; if ($docNombre === '') { $errors[] = 'El nombre del documento es obligatorio.'; } if (!$file || $file['error'] !== UPLOAD_ERR_OK) { $errors[] = 'Debe seleccionar un archivo válido.'; } else { // Validate extension $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($ext, ALLOWED_EXTENSIONS)) { $errors[] = 'Tipo de archivo no permitido. Permitidos: ' . implode(', ', ALLOWED_EXTENSIONS); } // Validate size if ($file['size'] > MAX_FILE_SIZE) { $errors[] = 'El archivo excede el tamaño máximo de ' . (MAX_FILE_SIZE / 1024 / 1024) . ' MB.'; } } if (empty($errors)) { // Create client upload directory $uploadDir = UPLOAD_DIR . 'client_' . $clienteId . '/'; if (!is_dir($uploadDir)) { mkdir($uploadDir, 0755, true); } // Generate unique filename $safeFilename = preg_replace('/[^a-zA-Z0-9_\-.]/', '_', $file['name']); $filename = time() . '_' . $safeFilename; $destPath = $uploadDir . $filename; if (move_uploaded_file($file['tmp_name'], $destPath)) { $rutaArchivo = 'client_' . $clienteId . '/' . $filename; $stmt = $db->prepare("INSERT INTO documentos (cliente_id, nombre, ruta_archivo, tipo) VALUES (?,?,?,?)"); $stmt->execute([$clienteId, $docNombre, $rutaArchivo, $ext]); header("Location: cliente-detalle.php?id=$clienteId&doc_saved=1#documentos"); exit; } else { $errors[] = 'Error al subir el archivo. Intente de nuevo.'; } } break; // ── Delete document ─────────────────────────────────────── case 'delete_document': if (!$isEdit) break; $docId = (int)($_POST['doc_id'] ?? 0); if ($docId > 0) { // Get file path before deleting $stmt = $db->prepare("SELECT ruta_archivo FROM documentos WHERE id=? AND cliente_id=?"); $stmt->execute([$docId, $clienteId]); $doc = $stmt->fetch(); if ($doc) { // Remove file from disk $filePath = UPLOAD_DIR . $doc['ruta_archivo']; if (file_exists($filePath)) { unlink($filePath); } // Remove DB record $stmt = $db->prepare("DELETE FROM documentos WHERE id=? AND cliente_id=?"); $stmt->execute([$docId, $clienteId]); } } header("Location: cliente-detalle.php?id=$clienteId&doc_deleted=1#documentos"); exit; } } // ── Handle download (GET) ─────────────────────────────────────── if (isset($_GET['download']) && $isEdit) { $docId = (int)$_GET['download']; $stmt = $db->prepare("SELECT nombre, ruta_archivo, tipo FROM documentos WHERE id=? AND cliente_id=?"); $stmt->execute([$docId, $clienteId]); $doc = $stmt->fetch(); if ($doc) { $filePath = UPLOAD_DIR . $doc['ruta_archivo']; if (file_exists($filePath)) { $mimeTypes = [ 'pdf' => 'application/pdf', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ]; $mime = $mimeTypes[$doc['tipo']] ?? 'application/octet-stream'; $downloadName = $doc['nombre'] . '.' . $doc['tipo']; header('Content-Type: ' . $mime); header('Content-Disposition: attachment; filename="' . $downloadName . '"'); header('Content-Length: ' . filesize($filePath)); header('Cache-Control: no-cache, must-revalidate'); readfile($filePath); exit; } } // File not found — continue to page with error $errors[] = 'Documento no encontrado.'; } // ── Load client data for edit mode ────────────────────────────── if ($isEdit) { $stmt = $db->prepare("SELECT * FROM clientes WHERE id=?"); $stmt->execute([$clienteId]); $cliente = $stmt->fetch(); if (!$cliente) { header('Location: clientes.php'); exit; } // Fetch credentials $stmtCred = $db->prepare("SELECT * FROM credenciales WHERE cliente_id=? ORDER BY created_at DESC"); $stmtCred->execute([$clienteId]); $credenciales = $stmtCred->fetchAll(); // Fetch documents $stmtDoc = $db->prepare("SELECT * FROM documentos WHERE cliente_id=? ORDER BY created_at DESC"); $stmtDoc->execute([$clienteId]); $documentos = $stmtDoc->fetchAll(); // Fetch tramites $stmtTram = $db->prepare("SELECT * FROM tramites WHERE cliente_id=? ORDER BY fecha_solicitud DESC, created_at DESC"); $stmtTram->execute([$clienteId]); $tramites = $stmtTram->fetchAll(); } // ── Pre-fill from solicitud if creating new ───────────────────── $fromSolicitud = 0; $prefill = ['nombre' => '', 'telefono' => '', 'email' => '', 'direccion' => '', 'notas' => '']; if (!$isEdit && isset($_GET['from_solicitud'])) { $fromSolicitud = (int)$_GET['from_solicitud']; if ($fromSolicitud > 0) { $stmt = $db->prepare("SELECT nombre, telefono, email FROM solicitudes WHERE id=?"); $stmt->execute([$fromSolicitud]); $sol = $stmt->fetch(); if ($sol) { $prefill['nombre'] = $sol['nombre']; $prefill['telefono'] = $sol['telefono'] ?? ''; $prefill['email'] = $sol['email'] ?? ''; } } } // If POST had errors, preserve submitted data if (!empty($errors) && ($_POST['action'] ?? '') === 'save_client') { $prefill['nombre'] = $_POST['nombre'] ?? ''; $prefill['telefono'] = $_POST['telefono'] ?? ''; $prefill['email'] = $_POST['email'] ?? ''; $prefill['direccion'] = $_POST['direccion'] ?? ''; $prefill['notas'] = $_POST['notas'] ?? ''; } // Success messages from redirects if (isset($_GET['saved'])) $success = 'Datos del cliente guardados correctamente.'; if (isset($_GET['cred_saved'])) $success = 'Credencial agregada correctamente.'; if (isset($_GET['cred_deleted'])) $success = 'Credencial eliminada.'; if (isset($_GET['doc_saved'])) $success = 'Documento subido correctamente.'; if (isset($_GET['doc_deleted'])) $success = 'Documento eliminado.'; $pageTitle = $isEdit ? 'Cliente: ' . ($cliente['nombre'] ?? '') : 'Nuevo Cliente'; ?>
| Portal | Usuario | Contraseña | Notas | Acciones |
|---|---|---|---|---|
| = htmlspecialchars($cred['portal']) ?> | = htmlspecialchars($cred['usuario']) ?> | •••••••• | = htmlspecialchars($cred['notas'] ?? '-') ?> |
No hay credenciales registradas para este cliente.
| Tipo | Estado | Fecha Solicitud | Fecha Cita | Precio | Acción |
|---|---|---|---|---|---|
| = htmlspecialchars($tipoLabels[$t['tipo']] ?? $t['tipo']) ?> | = $estadoLabels[$t['estado']] ?? htmlspecialchars($t['estado']) ?> | = $t['fecha_solicitud'] ? date('d/m/Y', strtotime($t['fecha_solicitud'])) : '-' ?> | = $t['fecha_cita'] ? date('d/m/Y', strtotime($t['fecha_cita'])) : '-' ?> | = $t['precio'] ? '$' . number_format($t['precio'], 2) : '-' ?> | Ver |
No hay trámites registrados para este cliente.
Crear primer trámite