CRM Hevea — Documentación Técnica

PHP 8.2 MySQL 8 XAMPP / Apache

📋 Visión general

Este CRM (Customer Relationship Management) es una aplicación web desarrollada en PHP + MySQL y alojada bajo Apache (XAMPP en local, VPS Ubuntu 24 con FastPanel en producción). Está diseñada específicamente para el sector del mobiliario (Hevea Mobiliario) y centraliza la gestión de leads, campañas publicitarias, clientes, productos, ferias y comunicación interna.

Características principales

  • Board tipo Kanban con columnas configurables para cada etapa del proceso comercial.
  • Sistema de roles: administradores con visión total y comerciales con vista restringida a sus propios leads.
  • Filtros combinados avanzados: por fecha, comercial, campaña, estado, ganado, remarketing y etiquetas.
  • Asignación rápida de comercial directamente desde la tarjeta sin abrir el lead.
  • Gestión de campañas y subcampañas con estadísticas de rendimiento.
  • Módulo de feria / checkin con tokens únicos por cliente.
  • Catálogo de productos SKU con búsqueda y administración de precios.
  • Sistema de backup automático a FTP y restauración.
  • Envío de correos vía PHPMailer con OAuth2 (Gmail).
  • Panel de mensajes internos entre usuarios.
  • Galería de imágenes con subida y gestión.
  • Dashboard con gráficas de rendimiento por comercial y campaña.
  • Temas visuales personalizados por usuario (claro, oscuro, etc.).

🛠️ Stack tecnológico

CapaTecnologíaVersión mínimaNotas
Servidor webApache2.4mod_rewrite activado
Lenguaje backendPHP8.2Extensiones: mysqli, mbstring, openssl, json
Base de datosMySQL / MariaDB8.0 / 10.6InnoDB, UTF8MB4
Entorno localXAMPP8.2+Windows, localhost
Entorno producciónVPS Ubuntu 24 + FastPanelHostinger
FrontendHTML5 + CSS3 + JavaScript (Vanilla)Sin framework JS; jQuery mínimo vía CDN
GráficasChart.js3.xCargado desde CDN
CorreoPHPMailer + OAuth26.xGmail API
Dependencias PHPComposer2.xvendor/ incluido
Excel/XLSXSimpleXLSXImportación de productos

👥 Roles y permisos

El sistema tiene dos roles definidos en la tabla users mediante el campo role_id.

🛠️ Administrador (role_id = 1)

  • Ve todas las órdenes, incluyendo sin asignar
  • Asigna y reasigna comerciales
  • Crea y edita campañas
  • Gestiona etiquetas globales
  • Accede al dashboard completo
  • Accede a backup, DB, productos, galería
  • Gestiona usuarios activos/inactivos
  • Filtra por cualquier comercial

👨‍💼 Comercial (role_id = 2)

  • Ve solo sus órdenes asignadas
  • Gestiona sus leads (notas, etapas, ganado)
  • Actualiza dirección, email, ingreso esperado
  • Marca órdenes como gestionadas / ganadas
  • Cambia su tema visual
  • Accede a mensajes internos
  • Sin acceso a backup ni DB
Control en backend En todos los endpoints PHP se verifica $_SESSION['role_id']. Los comerciales no pueden manipular parámetros para ver datos de otros usuarios porque el filtro WHERE comercial_id = $user_id se aplica en servidor.

📌 Board Kanban

La vista principal (index.php) muestra las órdenes distribuidas en columnas que representan etapas del proceso comercial. Las columnas se cargan desde la tabla columns y los cards se asignan a ellas mediante la tabla pivote column_orders.

Columnas típicas del board

📥 Nuevos
📞 Contactado
🔄 En proceso
📋 Presupuesto
⏳ Seguimiento
✅ Ganado
🔁 Remarketing
❌ Perdido

Interacciones del card

  • Click en card → abre el modal de detalle del lead (edit-ord.php).
  • Arrastrar card → mueve la orden a otra columna (llama a move_order.php vía fetch).
  • ⚠️ Sin asignar → widget inline de asignación rápida; clic en el badge despliega un select con comerciales activos y botón de confirmar (llama a edit_comercial.php).
  • Buscador → filtra tarjetas en tiempo real por nombre, email, teléfono, ciudad, país y badge "sin asignar".
  • Checkbox "Sin asignar" → muestra solo cards sin comercial asignado.

Navbar de filtros

El botón 🔍 Filtros abre un panel deslizante con todos los filtros combinables. Un badge numérico muestra cuántos filtros están activos. Al aplicar, se llama a filtro_combinado.php y el resultado reemplaza completamente el board mediante updateColumnsWithOrders(orders).

Rendimiento del board

Límite de 3 000 órdenes La query principal tiene LIMIT 3000. Si el volumen crece, considerar paginación lazy o virtualización de DOM.
  • Las etiquetas y campañas se pre-cargan en una sola query para evitar el problema N+1.
  • El archivo scripts/ine.js gestiona búsqueda, drag-and-drop y la apertura del modal.

📦 Órdenes / Leads

Una orden es la unidad central del CRM: representa un lead o cliente potencial que entra al embudo comercial. Cada orden pertenece a un comercial, puede tener etiquetas, notas, campaña, historial de etapas y datos de contacto.

Flujo de vida de una orden

Nueva / Sin asignar
Asignada a comercial
Contactada / Gestionada
Ganada ✅
Inactiva >15 días
Remarketing 🔁
o
Perdida ❌

Campos principales de una orden

idINT PKIdentificador único
titleVARCHARNombre / título del lead
empresaVARCHAREmpresa del cliente
email / phoneVARCHARDatos de contacto
comercial_idINT FKReferencia a users.id; NULL = sin asignar
client_idINT FKReferencia a clients.id
campanaINT FKReferencia a ncampanas.id
estadoTINYINT0 = no gestionada, 1 = gestionada
ganadoTINYINT0 = no ganada, 1 = ganada
expected_incomeDECIMALIngreso esperado en €
pagadoTINYINT1 si el pago fue registrado
fecha_subidoDATETIMEFecha de alta del lead
fecha_pagadoDATEFecha en que se marcó como pagado
remarktingTINYINT1 si fue enviado a remarketing manualmente
old_cmalVARCHAREmail previo (indica que es un re-lead)
prioridadINTNivel de prioridad (1-5)
is_checked_instagramTINYINTCheckbox de seguimiento Instagram
is_checked_linkedinTINYINTCheckbox de seguimiento LinkedIn
address / city / country / postal_codeVARCHARDatos de ubicación
detailsTEXTDescripción / detalles del lead

Notas / historial

Cada orden puede tener múltiples notas almacenadas en la tabla notes. Las notas se añaden desde el modal de detalle mediante add_note.php / save_or_update_note.php y se recuperan con get_notes.php.

Pasos recomendados al gestionar un lead

  1. Cambiar la etapa en el board según el momento del proceso.
  2. Marcar como gestionada (estado = 1) una vez contactada.
  3. Añadir una nota interna con el resumen de la conversación.
  4. Actualizar ingreso esperado si el cliente dio un presupuesto.
  5. Completar los datos de contacto que falten (email, ciudad, etc.).
  6. Asignar etiquetas relevantes para clasificación y búsqueda.
  7. Si se cierra el trato, marcar como ganada y registrar el ID de Timón en una nota.
  8. Si el lead procede de redes, marcar el checkbox de Instagram / LinkedIn.

🔍 Filtros combinados

El panel de filtros (botón 🔍 Filtros en el navbar) permite combinar múltiples criterios en una sola búsqueda. Se implementa mediante el endpoint filtro_combinado.php.

Filtros disponibles

FiltroParámetro JSONTipoDescripción
Fecha iniciofecha_iniciostring YYYY-MM-DDFiltra por fecha_subido
Fecha finfecha_finstring YYYY-MM-DDFiltra por fecha_subido
Comercialcomercial_idintSolo admin. 0 = todos
Sin asignarsin_asignarboolSolo admin. Fuerza comercial_id IS NULL
Campañacampana_idint0 = todas las campañas
Estadoestado-1 / 0 / 1-1 todos, 0 no gestionada, 1 gestionada
Ganadoganado-1 / 0 / 1-1 todos, 0 no ganada, 1 ganada
Remarketing activoremarketingboolFiltra por old_cmal NOT NULL
Fue a remarketingwas_remarketingboolFiltra por remarkting = 1
Etiquetasetiquetasint[]Órdenes con AL MENOS una de las etiquetas seleccionadas

Respuesta de filtro_combinado.php

{
  "success": true,
  "orders": [ { "order_id": 123, "title": "...", "tags": ["Interesado","VIP"], ... } ],
  "total": 47
}

Accesos rápidos de fecha

Botones de acceso rápido que pre-rellenan el rango: Hoy, 7 días, 30 días, 90 días. Implementados con la función fcSetDays(n).

Seguridad Los comerciales (role_id ≠ 1) tienen forzado WHERE o.comercial_id = $user_id independientemente de los parámetros enviados. No pueden acceder a datos de otros.

🏢 Clientes

El módulo de clientes (clientes/) permite registrar y gestionar las empresas o personas que están detrás de los leads.

Funciones disponibles

  • Listado paginado de clientes con búsqueda (clientes/index.php).
  • Crear cliente con nombre, empresa, email, teléfono, dirección, token único (clientes/create_client.php).
  • Editar datos de un cliente existente (clientes/update_client.php).
  • Eliminar cliente, con advertencia si tiene órdenes asociadas (clientes/delete_client.php).
  • Obtener clientes como JSON para selects dinámicos (clientes/get_clients.php, get_fclient.php).

Token único de cliente

Cada cliente tiene un token generado con bin2hex(random_bytes(16)). Este token se usa en el módulo de Feria para identificar al cliente en el checkin sin requerir login.

📣 Campañas

Las campañas (ncampanas) son el origen del lead: publicidad en redes, ferias, referencias, etc. Cada orden lleva asignada una campaña.

Gestión

  • Crear / editar campañas en crear_campanas/index.php o crear_campanas.php.
  • Cada campaña puede tener subcampañas (search_subcampanas.php).
  • El módulo campana/ permite ver todas las órdenes de una campaña concreta.
  • Las estadísticas de campañas se exponen en api/estadisticas_campanas.php.

Tabla ncampanas

idINT PKIdentificador
nombreVARCHARNombre de la campaña
descripcionTEXTDescripción opcional
activaTINYINT1 = activa, 0 = archivada

🏷️ Etiquetas

Las etiquetas (tags) permiten clasificar órdenes con categorías personalizadas: "VIP", "Presupuesto alto", "Interesado en X", etc.

Tablas involucradas

  • etiquetas: catálogo global de etiquetas (id, nombre, color).
  • orden_etiquetas: pivote que relaciona órdenes con etiquetas (id_orden, id_etiqueta).

Operaciones

  • Crear etiqueta: add_tag.php
  • Añadir etiqueta a orden: add_tag_to_order.php, add_tags_to_order.php
  • Eliminar etiqueta de orden: remove_tag_from_order.php
  • Obtener etiquetas de orden: get_order_tag.php, get_tags.php
  • Panel de administración de etiquetas: tag.php
  • Filtrar órdenes por etiqueta: filter_by_tags.php, o via filtro_combinado.php

📊 Dashboard

El dashboard (dashboard/index.php y dashboard.php) muestra métricas y gráficas de rendimiento del equipo comercial.

Métricas disponibles

  • Órdenes por comercial en un rango de fechas.
  • Comparativa de períodos (get_comparison_data.php).
  • Detalle de órdenes ganadas y perdidas.
  • Ingresos esperados vs reales por columna / etapa.
  • Rendimiento por campaña.

Endpoints del dashboard

POST dashboard/get_orders_details.php Detalle de órdenes con filtro de fechas y comercial

Parámetros: start_date, end_date, comercial_id (opcional)

POST dashboard/get_comparison_data.php Comparativa entre dos períodos

Parámetros: period1_start, period1_end, period2_start, period2_end

🎪 Feria / Checkin

El módulo de feria (feria/) permite gestionar la asistencia y el check-in de clientes a eventos o ferias comerciales mediante tokens únicos.

Flujo del módulo

  1. El administrador genera tokens para los clientes invitados: feria/generar_tokens_clients.php.
  2. Cada cliente recibe un enlace con su token único.
  3. Al llegar al evento, se escanea / accede al enlace: feria/checkin.php?token=XXX.
  4. El sistema registra la visita y la hora en la base de datos.
  5. El panel de administración (feria/panel.php) muestra en tiempo real quién ha hecho checkin.
  6. La vista de verificación (feria/veri.php) permite comprobar un token manualmente.
Seguridad de tokens Los tokens son generados con bin2hex(random_bytes(16)) — 32 caracteres hexadecimales aleatorios, prácticamente imposibles de adivinar.

📦 Catálogo de Productos SKU

El módulo productos-sku/ es un catálogo independiente de productos con SKU, descripción, precio y gestión de imágenes. Tiene su propio sistema de login.

Funcionalidades

  • Listado y búsqueda de productos por SKU o nombre (index.php, search_products.php).
  • Detalle de producto con imágenes y especificaciones (details.php).
  • Importación masiva desde archivo Excel/XLSX (upload.php, usa SimpleXLSX).
  • Edición de precio, descripción y estado (update_product.php).
  • Admin con acceso total (admin.php).
  • Cargar más con paginación lazy (load_more.php).
Login propio El módulo productos-sku tiene su propio login.php y session_check.php independientes del login principal del CRM.

🖼️ Galería

El módulo galeria/ permite subir, visualizar y eliminar imágenes asociadas a los recursos del CRM.

  • Ver galería: galeria/index.php
  • Subir imagen: galeria/upload.php — recibe archivo vía POST multipart.
  • Eliminar imagen: galeria/delete.php
  • Los estilos propios están en galeria/estilos.css.

💬 Mensajes internos

El módulo msg/ ofrece un sistema básico de mensajería interna entre usuarios del CRM.

  • Panel de mensajes: msg/index.php — lista de conversaciones.
  • Enviar mensaje: msg/cmsg.php — recibe destinatario y contenido.
  • Notificaciones: obtener_notificaciones.php — endpoint JSON para badge de mensajes no leídos.
  • verificarNotificaciones.php — verificación de nuevas notificaciones en polling.

💾 Backup y Restauración

El módulo backup/ permite generar, listar y restaurar copias de seguridad de la base de datos.

Flujo de backup

  1. Acceder a backup/index.php (solo admin).
  2. Pulsar "Generar backup" → llama a backup/backup.php que ejecuta mysqldump y guarda el .sql.gz.
  3. Los archivos se listan mediante backup/list_dirs.php.
  4. Para restaurar, seleccionar el archivo y confirmar → backup/restore.php.
⚠️ Precaución con Restore La restauración sobreescribe todos los datos actuales. Siempre genera un backup nuevo justo antes de restaurar uno antiguo.

FTP

El sistema puede configurarse para enviar el backup automáticamente a un servidor FTP externo. La conexión FTP se puede probar en backup/test_ftp.php.

📧 Correo electrónico

El envío de correos usa PHPMailer con autenticación OAuth2 / Gmail. Los archivos relacionados están en la raíz y en maile/ y PHPMailer/.

Archivos clave

ArchivoFunción
sendMail.phpFunción principal de envío; configura SMTP / OAuth
sendEmail.phpEndpoint alternativo de envío
email_helper.phpHelper con funciones reutilizables para construir emails
maile/send_email.phpEnvío desde el contexto del módulo maile
PHPMailer/get_oauth_token.phpGeneración / renovación del token OAuth2
pruebamail.phpTest de envío (solo desarrollo)
OAuth2 en producción El token OAuth2 expira. Si el envío de correos falla, regenerar el token ejecutando PHPMailer/get_oauth_token.php y actualizando las credenciales.

📁 Estructura de archivos

mpcrm/
├── index.php                  ← Board principal Kanban
├── dashboard.php              ← Redirige al dashboard
├── dashboard/                 ← Módulo dashboard
│   ├── index.php
│   ├── get_orders_details.php
│   └── get_comparison_data.php
├── db/                        ← Herramientas de base de datos (admin)
│   ├── connection.php         ← Conexión MySQLi (USAR ESTE)
│   ├── config_db.php          ← Credenciales DB
│   ├── index.php              ← Consola SQL visual
│   ├── ver_ordenes.php        ← Vista de todas las órdenes
│   └── export_excel.php       ← Exportación a Excel
├── clientes/                  ← CRUD de clientes
├── campana/                   ← Órdenes por campaña
├── crear_campanas/            ← Gestión de campañas
├── feria/                     ← Checkin de feria
├── productos-sku/             ← Catálogo de productos
├── galeria/                   ← Galería de imágenes
├── msg/                       ← Mensajería interna
├── backup/                    ← Backup y restauración
├── maile/                     ← Módulo de correo
├── padmin/                    ← Panel admin alternativo
├── cuenta/                    ← Perfil de usuario
├── registro/                  ← Registro de usuarios
├── scripts/                   ← JavaScript modular
│   ├── ine.js                 ← Board: búsqueda, drag, modal
│   └── main.js
├── api/                       ← Endpoints API JSON
│   ├── orders.php
│   ├── campanas.php
│   ├── leads.php
│   └── estadisticas_campanas.php
├── PHPMailer/                 ← Librería PHPMailer
├── vendor/                    ← Dependencias Composer
├── estilos/                   ← CSS globales adicionales
├── filtro_combinado.php       ← Filtro avanzado combinado
├── filter_orders.php          ← Filtro por fecha
├── filtro_campana.php         ← Filtro por campaña
├── filtro_remarketing.php     ← Filtro remarketing
├── add_order.php              ← Crear nueva orden
├── edit-ord.php               ← Modal de edición de orden
├── edit_comercial.php         ← Asignar comercial a orden
├── move_order.php             ← Mover orden a columna
├── mark_order_won.php         ← Marcar como ganada
├── marcar_gestionada.php      ← Marcar como gestionada
├── add_note.php               ← Añadir nota a orden
├── add_tag_to_order.php       ← Añadir etiqueta a orden
├── remove_tag_from_order.php  ← Quitar etiqueta de orden
├── update_order_stage.php     ← Actualizar etapa
├── update_priority.php        ← Actualizar prioridad
├── actualizar_pago.php        ← Registrar pago
├── assign_order.php           ← Asignar orden
├── get_order_details.php      ← Detalle orden (JSON)
├── get_comerciales.php        ← Lista comerciales activos
├── get_campanas.php           ← Lista campañas
├── get_tags.php               ← Lista etiquetas
├── buscar_ordenes.php         ← Búsqueda de órdenes
├── docs.html                  ← Esta documentación
└── connection.php             ← Alias de db/connection.php

🗄️ Base de datos

El archivo de conexión principal es db/connection.php. Usa MySQLi con prepared statements en todos los endpoints actualizados. Las credenciales se configuran en db/config_db.php.

Tablas principales

TablaDescripciónRelaciones clave
ordersÓrdenes / leads→ users, clients, ncampanas
usersComerciales y adminsrole_id → roles
clientsEmpresas / clientes← orders
ncampanasCampañas← orders.campana
columnsColumnas del board Kanban← column_orders
column_ordersPivote orden ↔ columnaorder_id, column_id
etiquetasCatálogo de etiquetas← orden_etiquetas
orden_etiquetasPivote orden ↔ etiquetaid_orden, id_etiqueta
notesNotas por orden→ orders, users
rolesDefinición de roles← users.role_id

Conexión a la base de datos

// db/connection.php
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
$conn->set_charset('utf8mb4');
if ($conn->connect_error) die("Error de conexión: " . $conn->connect_error);

Importar la base de datos

  1. Acceder a phpMyAdmin en localhost/phpmyadmin.
  2. Crear la base de datos si no existe: CREATE DATABASE mpcrm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  3. Seleccionar la BD → pestaña Importar → subir el archivo .sql.
  4. Si aparece error de foreign key constraint, ejecutar primero: SET FOREIGN_KEY_CHECKS=0; y al final SET FOREIGN_KEY_CHECKS=1;
  5. Si hay error de charset, asegurarse de que el archivo .sql incluya SET NAMES utf8mb4; al inicio.

Panel de base de datos (admin)

La ruta /db/index.php ofrece una interfaz visual para ejecutar consultas SQL, ver órdenes ganadas, exportar datos a Excel y otras utilidades de solo-admin.

🔌 API / Endpoints de referencia

Todos los endpoints devuelven Content-Type: application/json y requieren sesión activa. Los endpoints marcados con 🔒 requieren role_id = 1.

Órdenes

POST filtro_combinado.php Filtro avanzado combinado

Body JSON: { fecha_inicio, fecha_fin, comercial_id, campana_id, estado, ganado, sin_asignar, remarketing, was_remarketing, etiquetas[] }

Retorna: { success, orders[], total }

POST filter_orders.php Filtro simple por rango de fechas

Body JSON: { start_date, end_date }

GET get_order_details.php?id={id} Detalle completo de una orden

Retorna datos completos de la orden incluyendo notas, etiquetas y campaña.

POST move_order.php Mover orden a otra columna del board

Body JSON: { order_id, column_id }

POST edit_comercial.php Asignación rápida de comercial 🔒

Body JSON: { order_id, comercial_id }

POST mark_order_won.php Marcar orden como ganada

Body JSON: { order_id }

POST add_note.php Añadir nota a una orden

Body JSON: { order_id, note }

Clientes y campañas

GET get_comerciales.php Lista de comerciales activos (JSON)

Retorna: [ { id, username } ]

GET get_campanas.php Lista de campañas activas

Retorna: [ { id, nombre } ]

POST filtro_campana.php Filtro de órdenes por campaña y fechas

Body JSON: { campaign_id, start_date, end_date }

⚙️ Scripts JavaScript

ArchivoFunción principal
scripts/ine.jsMotor principal del board: drag & drop de cards, búsqueda en tiempo real, apertura del modal de edición, inicialización de la vista Kanban. Incluye el selector de búsqueda para .quick-assign-badge (leads sin asignar).
scripts/main.jsFunciones auxiliares globales del board.
main.js (raíz)Script principal cargado en algunas vistas fuera del board.

Funciones globales JS en index.php

FunciónDescripción
updateColumnsWithOrders(orders)Redibuja todo el board a partir de un array JSON de órdenes. Usada por el sistema de filtros.
qaShowForm(badge)Muestra el form inline de asignación rápida de comercial.
qaCancel(btn)Oculta el form inline de asignación.
qaSubmit(btn)Envía la asignación vía fetch a edit_comercial.php.
abrirFiltrosCombinado()Abre el panel deslizante de filtros.
cerrarFiltrosCombinado()Cierra el panel deslizante de filtros.
fcSetDays(n)Rellena el rango de fechas con los últimos N días.
fcAplicar()Recoge todos los filtros activos, llama a filtro_combinado.php y actualiza el board.
fcLimpiar()Resetea todos los filtros y recarga el board original.
fcContarActivos()Cuenta cuántos filtros están activos para el badge.

🔒 Seguridad

Autenticación y sesiones

  • Autenticación basada en sesiones PHP ($_SESSION['user_id'], $_SESSION['role_id']).
  • Todos los endpoints verifican la sesión al inicio; si no existe, devuelven HTTP 401 o redirigen al login.
  • Las contraseñas se almacenan con password_hash() y se verifican con password_verify().

Consultas SQL seguras

  • Todos los endpoints actualizados usan prepared statements MySQLi con bind_param().
  • Los parámetros se castean explícitamente ((int), trim()) antes de usar.
  • No se concatenan variables directamente en SQL.

Control de acceso por rol

  • Los comerciales solo pueden ver/modificar sus propias órdenes — el filtro WHERE comercial_id = $user_id se aplica en servidor, nunca depende del cliente.
  • Las rutas sensibles (/db/, /backup/, /padmin/) comprueban role_id == 1.

Recomendaciones pendientes

Áreas de mejora
  • Activar HTTPS en producción (certificado SSL en FastPanel).
  • Añadir protección CSRF en formularios POST críticos.
  • Limitar intentos de login (rate limiting).
  • Revisar ini_set('display_errors', 1) en filtro_campana.php y filter_orders.php — debe ser 0 en producción.
  • Mover db/config_db.php fuera del webroot o proteger con .htaccess.

🚀 Guía de despliegue

Entorno local (XAMPP)

  1. Copiar la carpeta del proyecto en C:\xampp\htdocs\mpcrm\.
  2. Importar el SQL en phpMyAdmin (ver sección Base de datos).
  3. Ajustar credenciales en db/config_db.php: host, usuario, contraseña, nombre de BD.
  4. Iniciar Apache y MySQL desde el panel de XAMPP.
  5. Acceder a http://localhost/mpcrm/.

Producción (VPS Ubuntu 24 + FastPanel)

  1. Subir los archivos modificados vía SFTP o FTP al directorio del dominio en el VPS.
  2. Verificar que db/config_db.php apunta a las credenciales de producción.
  3. Si hay cambios de esquema SQL, ejecutarlos en phpMyAdmin de producción.
  4. Limpiar la caché de OPcache si está activo: opcache_reset() o reiniciar PHP-FPM.
  5. Verificar permisos: chmod 644 en archivos PHP, 755 en directorios, 777 en directorios de subida (galería, backup).
  6. Comprobar que display_errors = Off en el php.ini de producción.

Archivos que NO deben subirse a producción

  • pruebamail.php — test de correo, expone credenciales.
  • test_conexion.php — expone configuración de BD.
  • opcache-status.php — información sensible del servidor.
  • debug.log, logs.txt, debug_edit_ord.txt — archivos de log de desarrollo.

Reiniciar FastPanel tras caída

ssh usuario@vps
systemctl restart fastpanel2
systemctl status fastpanel2

Hostname en VPS (cloud-init)

No editar /etc/hosts directamente en VPS con cloud-init En Ubuntu 24 con cloud-init, el archivo /etc/hosts se regenera en cada reinicio. Para añadir entradas permanentes, editar /etc/cloud/templates/hosts.debian.tmpl en lugar de /etc/hosts.

🔄 Flujo de trabajo diario

Para el comercial

  1. Acceder al board y revisar los leads nuevos en la columna de entrada.
  2. Contactar al lead y moverlo a la columna correspondiente.
  3. Añadir una nota con el resumen de la conversación.
  4. Marcar como gestionada una vez contactado.
  5. Actualizar ingreso esperado si el cliente dio un presupuesto.
  6. Completar los datos de contacto y ubicación que falten.
  7. Si se cierra el trato: marcar como ganada y registrar el ID de Timón en una nota.
  8. Si no hay respuesta tras varios intentos: mover a Perdido o Remarketing.

Para el administrador

  1. Revisar el dashboard para ver el rendimiento del equipo.
  2. Asignar leads sin comercial usando el widget de asignación rápida o desde la edición de la orden.
  3. Crear campañas nuevas para leads entrantes de nuevas fuentes.
  4. Usar el panel de filtros para análisis: leads de X campaña que no están ganados, etc.
  5. Generar backup semanal desde el módulo de backup.
  6. Revisar etiquetas y limpiar las que ya no se usen.

Preguntas frecuentes

¿Por qué no veo todas las órdenes en el board?

Si eres comercial, solo ves las órdenes asignadas a ti — esto es correcto. Si eres administrador y no ves alguna orden, puede que tenga un filtro activo. Revisa el badge de filtros en el navbar y pulsa "Limpiar" en el panel de filtros.

¿Cómo busco un lead por nombre o email?

Usa el campo de búsqueda en el navbar del board. Filtra en tiempo real por título, email, teléfono, ciudad, país y también por el texto "sin asignar".

¿Qué significa el badge numérico en el botón "🔍 Filtros"?

Indica cuántos filtros están activos actualmente. Si el badge muestra "2", hay 2 criterios de filtro aplicados sobre el board.

¿Por qué al aplicar un filtro el board cambia completamente?

Cuando se aplica un filtro combinado, el servidor devuelve solo las órdenes que cumplen los criterios, y el JavaScript redibuja el board completo con esas órdenes. Para volver a la vista normal, pulsa "Limpiar filtros".

¿Puedo asignar un comercial sin abrir el lead?

Sí. Si la tarjeta muestra "⚠️ Sin asignar", haz clic en ese badge — se desplegará un selector inline con todos los comerciales activos. Selecciona uno y pulsa "Asignar".

¿Qué pasa si marco una orden como ganada por error?

Un administrador puede desmarcarlo editando el campo ganado = 0 directamente desde /db/index.php o mediante la edición avanzada de la orden.

¿Cómo limpio la caché de la web en producción?

El servidor usa OPcache. Para vaciarlo: accede a /opcache-status.php (o elimina ese archivo si está en producción) o reinicia PHP-FPM desde FastPanel. A nivel de navegador, usa Ctrl+Shift+R para un hard reload.

¿Cómo regenero el token OAuth2 para el correo?

Accede a /PHPMailer/get_oauth_token.php en el navegador y sigue las instrucciones. Necesitarás las credenciales de la app de Google Cloud configurada.

¿Qué requisitos técnicos tiene el servidor?

  • PHP 8.2+ con extensiones: mysqli, mbstring, openssl, json, gd
  • MySQL 8.0+ o MariaDB 10.6+
  • Apache 2.4+ con mod_rewrite
  • Composer 2.x instalado
  • Acceso SFTP/SSH al servidor para despliegues

¿Dónde reporto un bug?

Contacta al desarrollador en lil2c@2cmonsterx.com o mediante los mensajes internos del CRM.