| Componente | Detalle |
|---|---|
| Backend | PHP puro (sin framework) con clases OOP: BaseModel + subclases (AgenteModel, BudgetModel, ContactModel, DocumentModel, EmailModel, ListModel, UserModel) |
| Frontend JS | jQuery 3.7.1 + Bootstrap 4/5 + Chart.js + ApexCharts + Select2 |
| CSS | app.css v2.0 con DM Sans, variables CSS (--color-primary, --color-sidebar, etc.), design system moderno con radios, sombras y layout responsivo |
| Base de datos | MySQL: conexiatec_adminpresupuestador |
| Autenticacion | Sesiones PHP: admin_session (portal admin) + agente_session (portal distribuidor). Hash SHA-256 + salt para passwords |
| API REST | distribuidor/api/index.php con auth via header X-API-Key contra tabla config_api_keys |
| Webhooks | HMAC-SHA256 con reintentos (backoff exponencial: 1m, 5m, 15m) y logs en BD |
| CSRF | Clase Security + csrf.js inyecta automaticamente token en todas las peticiones jQuery POST |
| Cifrado | AES-256-CBC via functions/crypt.php (openssl_encrypt con PBKDF2) |
| Timezone | Europe/Madrid configurado en config.php |
presupuestador_new/
admin/ <-- Portal administracion
clases/ <-- OOP: BaseModel.php, AgenteModel.php, BudgetModel.php,
ContactModel.php, DocumentModel.php, EmailModel.php,
ListModel.php, UserModel.php + legacy cls_*.php
php/ <-- Endpoints AJAX (~45 archivos _bd.php)
agentes_bd.php Gestion agentes/usuarios configurador
alertas_bd.php CRUD alertas
budget_bd.php Presupuestos admin
comerciales_bd.php Gestion comerciales
company_bd.php Datos empresa
configuracion_bd.php Config general unificada
contacts_bd.php CRUD clientes
email_templates_bd.php Templates email
familias_bd.php Familias de productos
producto_bd.php CRUD productos
subfamilias_bd.php Subfamilias
webhooks_bd.php Config webhooks admin
...
js/ <-- Frontend JS: app.js, csrf.js, budget.js, contacts.js, etc.
content/ <-- Vistas HTML parciales (configuraciones.php, etc.)
inc/ <-- Includes: _sidebar.php (UNIFICADO), _top.php, _footer.php
config/ <-- config.php (BD, constantes, includes funciones)
functions/ <-- crypt.php, Security.php, db.php, helpers.php, logins.php
migrations/ <-- SQL migrations
pdf/ <-- Generacion PDF: invoice.php, budget.php, border.php, advice.php
distribuidor/ <-- Portal operaciones
api/ <-- REST API: index.php (router principal)
php/ <-- Endpoints AJAX
index_bd.php Nuevo presupuesto (~93KB, archivo central)
presupuestos_bd.php Listado presupuestos + substatus
pipeline_bd.php Pipeline kanban distribuidores
actividades_bd.php Actividades comerciales
dashboard_director_bd.php KPIs direccion
estadisticas_bd.php Estadisticas detalladas
scoring_bd.php Gamificacion y puntos
permisos_helper.php Sistema RBAC (deteccion rol + permisos)
webhooks_helper.php Motor webhooks (dispatch, send, log, retry, API auth)
...
js/ <-- Frontend JS
index.js Nuevo presupuesto (familias, productos, carrito)
presupuestos.js Listado presupuestos
actividades.js Actividades comerciales
estadisticas.js Graficos estadisticas
realizar.js Realizar pedido
pedidos.js Gestion pedidos
...
inc/ <-- _topbar.php, _head.php, _sidebar.php (proxy a admin)
assets/css/ <-- app.css v2.0 (design system completo)
content/ <-- Vistas HTML: cobertura.php, etc.
sql/ <-- Migraciones SQL
pipeline.sql Columnas pipeline + tablas umbrales/log
actividades_comercial.sql Tabla actividades comerciales
scoring_actividad.sql Config scoring por actividad
sprint19_potenciales.sql Pipeline potenciales vs activos
zoho_sync_columns.sql Columnas sincronizacion Zoho
calendar_sync.sql Sincronizacion calendario
Portal Admin (/admin/): Configuracion, productos, familias, webhooks, alertas, clientes, agentes, comerciales, email templates.
Portal Distribuidor (/distribuidor/): Presupuestos, pipeline, actividades, estadisticas, scoring, cobertura, dashboard director.
Sidebar UNIFICADO: El archivo admin/inc/_sidebar.php se incluye desde ambos portales. Detecta automaticamente el contexto ($_inAdmin / $_inDist) via $_SERVER['REQUEST_URI'] y adapta los menus segun rol y permisos.
Comparten la BD MySQL conexiatec_adminpresupuestador. Sesiones separadas: admin_session vs agente_session.
Sistema RBAC implementado en distribuidor/php/permisos_helper.php con tabla config_roles que almacena permisos como JSON. Actualmente 4 roles:
| Rol | Deteccion | Acceso |
|---|---|---|
admin_super | Email @conexiatec.com o session admin activa | Todo el sistema (permiso wildcard *) |
director | Campo comerciales.role = 'director' (legacy) o agentes.rol = 'director' | Admin + distribuidor + config |
distribuidor | Agente no @conexiatec.com, no admin, con distribuidor asignado | Portal distribuidor: presupuestos, sus pedidos |
agente | Default (sin distribuidor, sin rol asignado) | Portal distribuidor limitado |
La funcion get_rol_usuario() sigue esta prioridad estricta:
agentes.rol esta vacio, busca en tabla comerciales por email. Soporte para directores/jefes historicos.@conexiatec.com y no tiene rol asignado en los pasos anteriores, se asigna admin_super.$_SESSION['admin_user'] tiene valor y no se detecto rol, se asigna admin_super.distribuidor.agente (sin permisos extra).Cache: El rol y los permisos se almacenan en $_SESSION['_rol_cache'] y $_SESSION['_permisos_cache'] con TTL de 5 minutos (time() - 300). Se limpian con limpiar_cache_permisos().
Almacenados en tabla config_roles como JSON en columna permisos. La funcion tiene_permiso($permiso) verifica contra el array decodificado. admin_super tiene siempre ['*'] (failsafe).
| Permiso | Controla acceso a |
|---|---|
pipeline.leer | Pipeline kanban + Pipeline presupuestos |
pipeline.editar | Mover tarjetas, editar etapas |
actividad.leer | Mi Actividad + Actividad Equipo |
actividad.crear | Registrar nuevas actividades |
stats.leer | Estadisticas avanzadas |
dashboard.director | Dashboard Director con KPIs ejecutivos |
admin.config | Seccion Administracion (roles, pipeline config, panel admin) |
distribuidor.leer | API: listar/ver distribuidores |
distribuidor.crear | API: crear prospecto |
distribuidor.editar | API: actualizar distribuidor |
distribuidor.activar | API: convertir prospecto a activo |
| Funcion | Uso |
|---|---|
tiene_permiso('pipeline.leer') | Verifica un permiso especifico |
tiene_algun_permiso(['stats.leer','dashboard.director']) | Verifica si tiene AL MENOS uno |
tiene_todos_permisos([...]) | Verifica si tiene TODOS |
es_rol('director') | Verifica rol exacto (acepta string o array) |
es_admin() | True si admin_super o director |
es_interno() | True si @conexiatec.com o admin session |
requiere_permiso('admin.config') | Bloquea y redirige a presupuestos.php si no tiene |
requiere_rol('director') | Bloquea y redirige si no tiene el rol |
get_usuario_info() | Devuelve array con email, nombre, rol, permisos, es_admin, es_interno |
[EXISTE] Cualquier email @conexiatec.com es admin_super automaticamente (detectado en paso 3 de get_rol_usuario()). Cualquier session admin activa tambien es admin_super (paso 4).
[FASE 0] Mejoras planificadas:
$_SESSION['nivel_simulado'] en permisos_helper.phpfunctions/crypt.php existente + libreria TOTPArchivos a modificar: distribuidor/php/permisos_helper.php (agregar logica nivel_simulado), nuevo endpoint en distribuidor/php/ para cambiar nivel con verificacion 2FA.
Todo el sistema de navegacion se controla desde un unico archivo: admin/inc/_sidebar.php. El sidebar detecta automaticamente si esta en /admin/ o /distribuidor/ y renderiza las secciones correspondientes.
Se muestra cuando $_inDist = true (URL contiene /distribuidor/).
| Menu | Archivo | Condicion |
|---|---|---|
| Presupuestos | presupuestos.php | Siempre visible (landing page) |
| Pipeline Ptos. | pipeline_presupuestos.php | pipeline.leer o admin |
| Nuevo Presupuesto | index.php | Siempre visible |
| Documentos | documentos.php | Siempre visible |
| Cobertura | cobertura.php | Siempre visible |
| Menu | Archivo | Condicion |
|---|---|---|
| Mi Actividad | actividades.php | actividad.leer o admin |
| Pipeline | pipeline.php | pipeline.leer o admin |
| Actividad Equipo | actividad_equipo.php | actividad.leer o admin |
| Menu | Archivo | Condicion |
|---|---|---|
| Dashboard Director | dashboard_director.php | dashboard.director o admin |
| Estadisticas | estadisticas.php | stats.leer o admin |
| Menu | Archivo | Condicion |
|---|---|---|
| Roles y Permisos | admin_permisos.php | admin.config o admin |
| Config Pipeline | pipeline_admin.php | admin.config o admin |
| Panel Admin | admin_bridge.php | admin.config o admin |
Se muestra cuando $_inAdmin = true (URL contiene /admin/ pero no /distribuidor/).
| Menu | Archivo |
|---|---|
| Dashboard Admin | index.php |
| Menu | Archivo |
|---|---|
| Usuarios Configurador | agentes.php |
| Potenciales | agentes.php?p=1 |
| Clientes | contacts1.php |
| Documentos Admin | documentos.php |
| Menu | Archivo | Descripcion |
|---|---|---|
| Panel Comercial | Enlace a distribuidor/presupuestos.php | Acceso al portal distribuidor |
| Configuraciones | configuraciones.php | Hub de configuracion (3 secciones) |
| Comerciales | comerciales.php | Gestion de comerciales |
| Webhooks | webhooks.php | Configuracion webhooks |
| Alertas | alertas.php | Sistema de alertas |
| Email Templates | email_templates.php | Plantillas email |
| Config General | configuracion.php | Substatus, SMTP, scoring, badges, objetivos |
| Status numerico | Nombre | Substatus posibles |
|---|---|---|
0 | EN CURSO | PENDING |
1 | TRAMITADOS | TRAMITADO |
2 | RECHAZADOS | RECHAZADO, CADUCADO, TIENE PERMANENCIA |
| Accion | Parametro GET | Descripcion |
|---|---|---|
| Listar presupuestos | readPresupuestos={status} | Lista por status (0/1/2) con filtros q, f1, f2. LIMIT 500. No carga campos longtext. |
| Cambiar substatus | updateSubstatus={uuid} | POST con substatus. Si TRAMITADO: status=1 + dteTramitado=NOW(). Si RECHAZADO/CADUCADO: status=2. |
Al cambiar substatus: _wh_fire($res, 'substatus_cambiado', [...]) - funcion inline que busca en config_webhook y envia con HMAC-SHA256, timeout 5s/2s connect, log a webhook_log.
| # | Paso | Endpoint / Funcion |
|---|---|---|
| 1 | Seleccionar tipo: PARTICULARES (uuid: 2b3343ba...) o AUTONOMOS Y EMPRESAS (uuid: 8ce373...) | php/index_bd.php?setTipo={uuid} |
| 2 | Cargar familias del tipo seleccionado. Empresas: 8 familias (Pack Fibra, Conectividad, Linea Movil, PBX VoIP, IA, TIME, eSIM, TV). Particulares: 4 familias. | GET php/index_bd.php?readFamilias={tipoUUID} |
| 3 | Click en familia: cargar productos de esa familia y tipo | GET php/index_bd.php?readProducts={familiaUUID}&tipo={tipoUUID} |
| 4 | Tarjeta de producto: imagen, nombre (h3), descripcion, servicios adicionales (checkboxes con precio), campo direccion/observacion, precio, boton ANADIR, +info (base64) | Renderizado en HTML por index_bd.php |
| 5 | Anadir producto al carrito con extras (checkboxes), cantidad, extensiones | POST php/index_bd.php?add=1 con uuid, tipo, extras[], qty, extensiones, precioExtensiones |
| 6 | Leer carrito: DOS carritos separados (servicios + alarmas) | GET php/index_bd.php?readCarrito=1 (servicios) + readCarrito=1&ALARMA=1 (alarmas) |
| 7 | Logica IVA en JS (readCarrito): - Particular (uuid 2b3343...): precios con IVA incluido - Empresa (uuid 8ce373...): precios sin IVA + total con IVA (*1.21) | Logica en index.js funcion readCarrito() |
| 8 | Modificar precio del producto en carrito | POST php/index_bd.php?updatePrecio=1 con uuid, precio |
| 9 | Modificar descuento | POST php/index_bd.php?updateDTO=1 con uuid, dto |
| 10 | Modificar cantidad | POST php/index_bd.php?updateCantidad=1 con uuid, cantidad |
| 11 | Eliminar producto del carrito | GET php/index_bd.php?remove={uuid} |
| 12 | Formulario cliente: Cliente potencial*, Contacto, Email, Movil, Observaciones, Agente (Select2) | Campos en formulario HTML |
| 13 | Guardar presupuesto | POST php/index_bd.php?savePTO=1 con nombreCliente, nombreContacto, nombreEmail, nombreMovil, nombreObservaciones, nombreAgente |
| 14 | Redirect automatico a presupuestos.php | JavaScript redirect post-save |
[FASE 0] Mejoras planificadas para Nuevo Presupuesto:
add=1 y updatePrecio=1) que impida precio < precio_costereadCarrito(), reemplazar hardcoded *1.21 por porcentaje segun CP del distribuidor (35xxx/38xxx=IGIC 7%, 51xxx/52xxx=IPSI). Requiere campo tipo_impuesto en config_edistribuidor| Vista | Etapas (fallback si no hay BD) | dist_tipo filtrado |
|---|---|---|
| Potenciales | prospecto (#6b7280) → contactado (#3b82f6) → negociacion (#f59e0b) → propuesta (#fb923c) → onboarding (#8b5cf6) | potencial |
| Activos | activo (#10b981) → top (#8b5cf6) → en_riesgo (#ef4444) | activo |
Las etapas se cargan dinamicamente desde tabla config_pipeline_etapas (company, pipeline_tipo, nombre, codigo, color, orden, es_final). Si la tabla esta vacia, se usan los fallback hardcoded.
config_edistribuidor - campos: pipeline_etapa, pipeline_etapa_manual, dist_tipoconfig_pipeline_etapas - definicion de etapasconfig_pipeline_umbrales - reglas automaticas (primera_actividad, presupuestos_mes, dias_sin_presupuesto, etc.)pipeline_log - historial de cambios de etapa con distribuidor_uuid, etapa_anterior, etapa_nueva, motivo, usuario| action | Metodo | Descripcion |
|---|---|---|
board | GET | Kanban board filtrado por vista (potenciales/activos) + filtro comercial |
move | POST | Mover tarjeta entre etapas (drag & drop) |
recalc | GET | Recalcular etapas automaticas segun umbrales |
log | GET | Historial cambios de un distribuidor |
reset_manual | POST | Devolver etapa a calculo automatico |
ficha | GET | Ficha completa del distribuidor |
crear_potencial | POST | Crear prospecto nuevo |
editar_potencial | POST | Editar datos potencial |
convertir | POST | Convertir potencial a activo |
stats | GET | Contadores por vista |
nota | llamada | visita | email | reunion
contactado | interesado | no_interesado | no_disponible
actividad_registrada)actividad_visita)Campos: siguiente_accion_fecha, siguiente_accion_tipo, siguiente_accion_completada
| action | Descripcion |
|---|---|
list | Listar actividades con filtros: periodo (hoy/semana/mes), tipo, comercial, dist_uuid, busqueda texto. Admin ve todas; no-admin solo las suyas. LIMIT 200. |
stats | KPIs del comercial: actividades hoy, ayer, variacion |
cartera | Cartera del comercial |
quehacer | Agenda de pendientes |
save | POST: registrar nueva actividad |
completar_siguiente | POST: marcar siguiente accion como completada |
historial | Historial por distribuidor |
| action | KPIs / Datos |
|---|---|
resumen | Facturacion mes/ano, objetivo anual (8M por defecto), forecast, promedio mensual, tramitaciones mes, presupuestos mes, conversion 90d, actividades semana, distribuidores activos vs total, comerciales activos, KPIs captacion potenciales (total, conversiones mes, nuevos mes, actividades potenciales semana) |
equipo | Rendimiento por comercial |
pipeline | Distribucion por etapa (potenciales + activos) |
evolucion | Ultimos 12 meses |
alertas | Alertas activas |
top_dist | Top distribuidores por facturacion |
Soporta autenticacion por session Y por API key (para n8n via X-API-Key).
year (default: ano actual)from / to (override year)getAuthComercial()| action | Datos |
|---|---|
kpis | Total presupuestos, en_curso, tramitados, rechazados, valores, ticket medio, conversion |
monthly | Desglose mensual (12 meses) |
by_comercial | Agrupado por comercial |
by_distribuidor | Agrupado por distribuidor |
by_estado | Distribucion por estados |
trend | Tendencia temporal |
distribution | Distribucion de valores |
comparison | Comparativa interanual |
timerange | Rango temporal personalizado |
| Accion | Puntos | Descripcion |
|---|---|---|
presupuesto_creado | Configurable en BD | Al crear presupuesto |
actividad_registrada | 3 | Registrar actividad comercial |
actividad_visita | 5 | Bonus por visita presencial |
login_diario | Configurable | Primer login del dia (prevencion duplicados) |
Tipos de condicion: puntos_total (suma total ≥ valor), tram_total (tramitaciones totales), ptos_mes (presupuestos este mes), conversion_alta (% conversion con min 10 presupuestos), racha_dias (dias consecutivos activo).
Evaluacion automatica via checkBadges() despues de cada registro de puntos.
| action | Descripcion |
|---|---|
leaderboard | Top 50 agentes con puntos, badges, distribuidor. Filtro periodo: mes/ano/total |
mis_puntos | Puntos del agente: total, mes, semana, hoy, rank, racha, next_badge |
mis_badges | Badges desbloqueados + bloqueados del agente |
todos_badges | Catalogo completo de badges |
historial | Historial de puntos recientes (max 50) |
config_scoring | Configuracion de puntos por accion (solo admin) |
registrar_puntos | Registrar puntos manualmente (admin/sistema/webhook) |
check_badges | Evaluar y otorgar badges pendientes |
stats_gamificacion | Stats globales: puntos total, mes, agentes participando, badges otorgados, accion top |
[EXISTE] Pagina con iframe embebido de https://iframe.api-eligibility.fr/?i=2159 (proveedor frances de consulta de elegibilidad). Auto-redimensionamiento de altura via postMessage.
[FASE 0] Multi-proveedor planificado:
Implementacion: Nuevo archivo distribuidor/php/cobertura_bd.php con 3 funciones de busqueda. Frontend muestra tabla comparativa de disponibilidad por proveedor.
Trigger adicional: Al rellenar campo direccion en formulario de nuevo presupuesto (distribuidor/js/index.js), disparar busqueda automatica.
Pagina principal del portal admin con calendario de eventos.
Hub central de configuracion organizado en 3 secciones:
| Enlace | Archivo | Descripcion |
|---|---|---|
| Conexia | c_company.php | Datos de la empresa |
| Agentes comerciales | c_edistribuidor.php | Distribuidores y comerciales |
| Agentes potenciales | c_edistribuidor.php?p=1 | Agentes en proceso |
| Tipo acciones | c_acciones.php | Tipos de acciones del sistema |
| Usuarios ADMIN/MASTER | c_usuarios.php | Gestion de accesos |
| Estado pedidos | c_estadopedidos.php | Estados del workflow |
| IVA | c_iva.php | Tipos impositivos |
| Unidades | c_unidades.php | Unidades de medida |
| Enlace | Archivo | Descripcion |
|---|---|---|
| Productos | c_producto.php | Catalogo de productos |
| Anexos | c_anexos.php | Documentos anexos |
| Tipos | c_tipos.php | Clasificacion de productos |
| Familias | c_familias.php | Familias de productos |
| Subfamilias | c_subfamilias.php | Subcategorias |
| Enlace | Archivo | Descripcion |
|---|---|---|
| Categorias | c_catdocs.php | Categorias de documentos |
| Tipo documentos | c_tipodocumentos.php | Tipos de documentos |
| Documentos | documentos.php | Todos los documentos |
[EXISTE] Campos: Referencia, Nombre, Precio ALTA con IVA, Dto ALTA %, Precio con IVA, Dto %, Subvencion gobierno, imagenes, servicios extras (JSON), y mas. 36 productos con mapeo DoWISP (dowisp_producto_id, dowisp_tarifa_id).
[FASE 0] Agregar campo es_servicio (BOOLEAN DEFAULT false) para distinguir productos puntuales de servicios recurrentes, necesario para calculo MRR.
SQL: ALTER TABLE product ADD COLUMN es_servicio BOOLEAN DEFAULT false;
Archivo UI a modificar: admin/php/producto_bd.php (agregar campo en save/load)
[EXISTE] Campos actuales: Nombre, Tipo (AUTONOMOS Y EMPRESAS / PARTICULARES / PRUEBA), Orden, Descripcion, imagen.
[FASE 0] Agregar campos de margen:
ALTER TABLE config_familias ADD COLUMN margen_master_pct DECIMAL(5,2) NULL;
ALTER TABLE config_familias ADD COLUMN margen_distrib_pct DECIMAL(5,2) NULL;
Igualmente en subfamilias (NULL = hereda de familia):
ALTER TABLE config_subfamilias ADD COLUMN margen_master_pct DECIMAL(5,2) NULL;
ALTER TABLE config_subfamilias ADD COLUMN margen_distrib_pct DECIMAL(5,2) NULL;
Herencia de margenes: Producto.margen ?? Subfamilia.margen ?? Familia.margen
Archivos a modificar: admin/php/familias_bd.php, admin/php/subfamilias_bd.php
[EXISTE] Sistema completo:
webhook_dispatch() en webhooks_helper.php - busca suscriptores, prepara payload, envia con cURL, firma HMAC-SHA256, log a log_webhookswebhook_schedule_retry(){{evento}}, {{datos.campo}}presupuesto_creado, substatus_cambiado, distribuidor.creado, distribuidor.actualizado, distribuidor.activado, agente.creado[FASE 0] Configurar URLs destino (CRM Conexia + n8n Skynet) + agregar eventos:
pedido.tramitado - cuando pedido se tramita (contrato DoWISP creado)lead.creado - nuevo lead en pipelinelead.etapa_cambiada - lead cambia de etapa en KanbanImplementacion: INSERT en config_webhook con URLs destino + agregar llamadas a webhook_dispatch() en los puntos del codigo donde ocurren estos eventos (index_bd.php para tramitacion, pipeline_bd.php para leads).
Tipos de alerta: budget (presupuestos), activity (actividad), performance (rendimiento). Gestion via admin/php/configuracion_bd.php (action=alertas, alerta_toggle).
Campos: nombre, tipo (sistema/custom), subject, body_html, body_text, variables (JSON con placeholders disponibles). Envio via admin/php/MailerService.php usando PHPMailer.
[EXISTE] Configuracion unificada con secciones accesibles via parametro action:
config_estadopedidosconfig_objetivos)[FASE 0] Cifrar token DoWISP almacenado en BD usando phpencrypt() / phpdecrypt() de admin/functions/crypt.php (AES-256-CBC via openssl con PBKDF2). Actualmente el token 2edb962a... esta en texto plano.
[EXISTE] Campos: ID CRM, Nombre, DNI, Email, Password (hash SHA-256 + salt), Puede modificar precios (SI/NO), IVA (SI/NO), Agente comercial, Distribuidor (edistribuidor UUID), activo/inactivo.
[FASE 0] Agregar campo rol (dropdown con roles de config_roles) para asignacion directa de rol desde panel admin, sin depender de deteccion automatica.
Archivo a modificar: admin/php/agentes_bd.php
[EXISTE] Campos: Nombre comercial, NIF, Contacto, Email, Telefono. Tabs: Facturacion | Contacto General. Sub-endpoints: comments, contactslist, direcciones, documents.
[FASE 0] Agregar vinculacion con DoWISP:
ALTER TABLE contacts ADD COLUMN dowisp_cliente_id INT NULL;
ALTER TABLE contacts ADD INDEX idx_dowisp_id (dowisp_cliente_id);
Logica al tramitar: Buscar contacto.dowisp_cliente_id. Si NULL, buscar en DoWISP por CIF. Si existe, guardar ID. Si no existe, crear via POST /clientes/.
Archivos a modificar: admin/php/contacts_bd.php (agregar campo), distribuidor/php/index_bd.php (logica tramitacion)
La funcion api_authenticate() en webhooks_helper.php busca la API key en config_api_keys, verifica IP whitelist si configurada, actualiza ultimo_uso y total_peticiones, y decodifica los permisos JSON.
| Metodo | Endpoint | Permiso requerido | Descripcion |
|---|---|---|---|
| GET | distribuidor/listar | distribuidor.leer | Listar distribuidores por dist_tipo (default: activo). Paginacion limit/offset (max 500). |
| GET | distribuidor/ver/{uuid} | distribuidor.leer | Detalle distribuidor con sus agentes vinculados. |
| POST | distribuidor/crear | distribuidor.crear | Crear prospecto (dist_tipo='potencial', origen='api'). Dispara webhook distribuidor.creado. |
| PUT/POST | distribuidor/actualizar | distribuidor.editar | Actualizar campos (nombre, email, phone, town, province, cif, comercial, scoring, notas_comercial). Dispara webhook distribuidor.actualizado. |
| POST | distribuidor/activar | distribuidor.activar | Convertir prospecto a activo: crea agente con password temporal, actualiza dist_tipo='activo'. Dispara webhooks distribuidor.activado + agente.creado. |
| GET | presupuesto/listar | - | Listar presupuestos (endpoint registrado en router pero no implementado como funcion independiente). |
| GET | stats/resumen | stats.leer | Resumen: distribuidores por tipo + presupuestos del mes (total + tramitados). |
curl -H "X-API-Key: tu_api_key" \
"https://pedidos.conexiatec.com/presupuestador_new/distribuidor/api/index.php?endpoint=distribuidor/listar&dist_tipo=activo&limit=50"
Implementacion: Agregar cases en el switch de distribuidor/api/index.php + funciones handler correspondientes.
| Metodo | Endpoint | Descripcion |
|---|---|---|
| GET | presupuesto/ver/{uuid} | Detalle presupuesto con lineas de productos |
| GET | pedido/listar | Listar pedidos con filtros |
| GET | pedido/ver/{uuid} | Detalle pedido |
| GET | contacto/listar | Listar contactos |
| GET | contacto/ver/{uuid} | Detalle contacto con dowisp_cliente_id |
| POST | contacto/buscar | Buscar por CIF/email (datos sensibles via POST body, nunca en URL) |
| GET | producto/listar | Catalogo activo completo |
| GET | agente/listar | Listar agentes |
| GET | pipeline/board | Estado pipeline actual (potenciales + activos) |
| POST | webhook/test | Verificar conectividad webhook |
[EXISTE] Integracion activa:
auto_contratacion=true, auto_firma=truedowisp_producto_id y dowisp_tarifa_id2edb962a5c8976e4f5dfda9939b68ca3016b48bc (actualmente en texto plano en BD)conexiatec.dowisp.com/api//contratador/crea_contrato/ en DoWISP/api/clientes/ (1569 clientes). Suscripciones, facturas, tickets devuelven 404.[FASE 0] Mejoras:
phpencrypt() de functions/crypt.phpArchivo principal a modificar: distribuidor/php/index_bd.php (logica de tramitacion)
[EXISTE] Sistema completo en produccion:
webhook_dispatch()X-Webhook-SignatureX-Webhook-Timestamplog_webhooks + log_eventos (evento central)Eventos que ya se disparan:
presupuesto_creado - al guardar nuevo presupuestosubstatus_cambiado - al cambiar substatus (via _wh_fire() inline en presupuestos_bd.php)distribuidor.creado / distribuidor.actualizado / distribuidor.activado - via APIagente.creado - al activar distribuidor via API[FASE 0] Agregar:
skynet-n8n.conexiatec.com)pedido.tramitado, lead.creado, lead.etapa_cambiadaconfig_webhook + agregar webhook_dispatch() en index_bd.php (tramitacion) y pipeline_bd.php (leads){
"evento": "presupuesto_creado",
"timestamp": "2026-04-09T10:30:00+02:00",
"ref_tipo": "presupuesto",
"ref_uuid": "abc123...",
"datos": {
"uuid": "abc123...",
"cliente": "Empresa SL",
"total": 1200.00,
...
}
}
3 proveedores a integrar:
| Proveedor | Metodo | Fuente |
|---|---|---|
| Sarenet | Busqueda en CSV local | CSV de nodos Sarenet (disponible) |
| Onivia B2B | API REST v2.3 | Documentacion "AI - Onivia B2B - Coverage v2.3.docx" |
| CableMovil/Orange | Consulta web | orange.es/contratacion/cobertura |
Implementacion:
Todos los cambios son ALTER TABLE aditivos (columnas NULL o con DEFAULT). No rompen queries existentes. Rollback: DROP COLUMN.
-- =============================================
-- 1. Vincular contactos con DoWISP
-- =============================================
ALTER TABLE contacts ADD COLUMN dowisp_cliente_id INT NULL;
ALTER TABLE contacts ADD INDEX idx_dowisp_id (dowisp_cliente_id);
-- =============================================
-- 2. Margenes en familias
-- =============================================
ALTER TABLE config_familias ADD COLUMN margen_master_pct DECIMAL(5,2) NULL;
ALTER TABLE config_familias ADD COLUMN margen_distrib_pct DECIMAL(5,2) NULL;
-- =============================================
-- 3. Margenes en subfamilias (herencia: NULL = hereda de familia)
-- =============================================
ALTER TABLE config_subfamilias ADD COLUMN margen_master_pct DECIMAL(5,2) NULL;
ALTER TABLE config_subfamilias ADD COLUMN margen_distrib_pct DECIMAL(5,2) NULL;
-- =============================================
-- 4. Producto: flag servicio para calculo MRR
-- =============================================
ALTER TABLE product ADD COLUMN es_servicio BOOLEAN DEFAULT false;
-- =============================================
-- 5. Motor IGIC/IPSI en distribuidores
-- =============================================
ALTER TABLE config_edistribuidor ADD COLUMN tipo_impuesto
ENUM('IVA','IGIC','IPSI') DEFAULT 'IVA';
ALTER TABLE config_edistribuidor ADD COLUMN porcentaje_impuesto
DECIMAL(5,2) DEFAULT 21.00;
-- =============================================
-- 6. Jerarquia organizativa (ampliar distribuidores)
-- =============================================
ALTER TABLE config_edistribuidor ADD COLUMN parent_uuid VARCHAR(40) NULL;
ALTER TABLE config_edistribuidor ADD COLUMN org_tipo
ENUM('conexia','master','distribuidor','delegacion') DEFAULT 'distribuidor';
ALTER TABLE config_edistribuidor ADD COLUMN observaciones_privadas TEXT NULL;
ALTER TABLE config_edistribuidor ADD INDEX idx_parent (parent_uuid);
Nota de seguridad: Todos los cambios son aditivos. Columnas nuevas tienen valor NULL o DEFAULT, lo que significa que las queries existentes que no referencian estas columnas seguiran funcionando exactamente igual. Para rollback: ALTER TABLE {tabla} DROP COLUMN {columna};
| Medida | Implementacion | Archivo(s) |
|---|---|---|
| Prepared statements (PDO) | BaseModel y todas las subclases usan queries parametrizadas. API usa $db->prepare() con bind_param(). |
admin/clases/BaseModel.php, distribuidor/api/index.php, webhooks_helper.php |
| CSRF tokens | Clase Security::generateCsrfToken() genera token (bin2hex random_bytes(32)). csrf.js lo inyecta automaticamente en header X-CSRF-Token de todo POST jQuery. Verificacion: Security::verifyCsrfToken() con hash_equals(). |
admin/functions/Security.php, admin/js/csrf.js |
| HMAC-SHA256 webhooks | Firma del payload JSON con secret_key del webhook. Header X-Webhook-Signature + X-Webhook-Timestamp. |
distribuidor/php/webhooks_helper.php |
| Session auth | Verificacion de sesion en config.php - redirige a login si sesion invalida. Funciones admin_check() y agente_check(). |
admin/config/config.php, admin/functions/logins.php |
| X-API-Key auth | API keys en tabla config_api_keys con IP whitelist, permisos JSON, rate_limit, total_peticiones, ultimo_uso. |
distribuidor/php/webhooks_helper.php (api_authenticate()) |
| Cifrado AES-256-CBC | phpencrypt() / phpdecrypt() con PBKDF2 (SHA-256, PARAMS_PASSPHRASE, PARAMS_SALT, PARAMS_ITERATIONS). |
admin/functions/crypt.php |
| Soft deletes | Campo deleted = 0/1 en todas las tablas. Nunca borrado fisico. |
Todas las queries incluyen WHERE deleted = 0 |
| Company-level isolation | Campo company en todas las tablas. Todas las queries filtran por company del usuario autenticado. |
Todos los archivos _bd.php |
| XSS prevention | Security::escape() para output HTML. htmlspecialchars() en sidebar. |
admin/functions/Security.php |
| Audit log | evento_log() registra evento, ref_tipo, ref_uuid, datos_antes, datos_despues, usuario, origen, IP en tabla log_eventos. |
distribuidor/php/webhooks_helper.php |
| API request logging | Tabla log_api_requests: api_key_uuid, endpoint, metodo, IP, request_body, response_code, duracion_ms. |
distribuidor/api/index.php |
| Password hashing | SHA-256 + salt aleatorio (bin2hex random_bytes(16)). Salt almacenado en tabla agentes. | distribuidor/api/index.php (activar distribuidor) |
| Medida | Implementacion | Archivo(s) a modificar |
|---|---|---|
| 2FA TOTP | Obligatorio para escalar a Super Admin desde nivel inferior. Usar TOTP library + functions/crypt.php para almacenar secret. |
distribuidor/php/permisos_helper.php + nuevo endpoint |
| Cifrado token DoWISP | Cifrar con phpencrypt(), descifrar con phpdecrypt() al usar. |
admin/php/configuracion_bd.php, distribuidor/php/index_bd.php |
| Regla de Oro | Validacion servidor: if ($precio < $precio_coste) reject() en endpoints add y updatePrecio. |
distribuidor/php/index_bd.php |
| Rate limiting | En login y API. Actualmente hay TODO en api_authenticate() para implementar con Redis. |
distribuidor/php/webhooks_helper.php, login.php |
Regla critica: NIF, email, IBAN, tokens y datos sensibles SIEMPRE deben enviarse por POST body, NUNCA en URL. El endpoint contacto/buscar usa POST precisamente por esto.
| Tarea | Descripcion | Archivo(s) | Dependencia |
|---|---|---|---|
| Ampliar API | 10 nuevos endpoints en el switch del router (presupuesto/ver, pedido/listar, pedido/ver, contacto/listar, contacto/ver, contacto/buscar, producto/listar, agente/listar, pipeline/board, webhook/test) | distribuidor/api/index.php | Ninguna |
| dowisp_cliente_id | ALTER TABLE contacts + INDEX | SQL directo en BD | Ninguna |
| Cifrar token DoWISP | Usar phpencrypt() para cifrar el token en BD. Modificar lectura para usar phpdecrypt(). | admin/php/configuracion_bd.php, distribuidor/php/index_bd.php | Ninguna |
| Tarea | Descripcion | Archivo(s) | Dependencia |
|---|---|---|---|
| Margenes familias/subfamilias | ALTER TABLE config_familias + config_subfamilias (4 columnas nuevas) | SQL + admin/php/familias_bd.php, subfamilias_bd.php | Ninguna |
| es_servicio | ALTER TABLE product ADD COLUMN | SQL + admin/php/producto_bd.php | Ninguna |
| tipo_impuesto / parent_uuid | ALTER TABLE config_edistribuidor (5 columnas + 1 indice) | SQL + admin/php/edistribuidor_bd.php | Ninguna |
| Roles en config_roles | Agregar roles nuevos, asignar permisos granulares | INSERT en config_roles |
Ninguna |
| Tarea | Descripcion | Archivo(s) | Dependencia |
|---|---|---|---|
| Logica dowisp_cliente_id | Al tramitar: buscar/crear cliente en DoWISP, guardar ID en contacts | distribuidor/php/index_bd.php | Semana 1 (dowisp_cliente_id) |
| Motor IGIC/IPSI | En readCarrito(), reemplazar *1.21 hardcoded por % segun tipo_impuesto del distribuidor | distribuidor/js/index.js | Semana 2 (tipo_impuesto) |
| Regla de Oro | Validacion servidor precio >= precio_coste en add=1 y updatePrecio=1 | distribuidor/php/index_bd.php | Ninguna |
| Configurar webhooks | INSERT URLs destino en config_webhook + agregar webhook_dispatch() en tramitacion y pipeline | distribuidor/php/index_bd.php, pipeline_bd.php | Semana 1 (API para test) |
| Switch de nivel + 2FA | $_SESSION['nivel_simulado'] + endpoint 2FA | distribuidor/php/permisos_helper.php + nuevo endpoint | Semana 2 (roles) |
| Archivo | Cambio | Semana |
|---|---|---|
| distribuidor/api/index.php | Agregar 10 endpoints nuevos (cases en switch + funciones handler) | 1 |
| distribuidor/php/index_bd.php | Regla de Oro (validacion precio >= coste en add=1, updatePrecio=1). Logica dowisp_cliente_id al tramitar. Descifrar token DoWISP al usar. | 1, 3 |
| distribuidor/js/index.js | Motor IGIC/IPSI: reemplazar *1.21 hardcoded en readCarrito() por porcentaje dinamico. Trigger cobertura al rellenar direccion. |
3 |
| distribuidor/php/permisos_helper.php | Agregar soporte $_SESSION['nivel_simulado'] en get_rol_usuario(). Restringir admin_super a emails especificos. |
3 |
| distribuidor/php/pipeline_bd.php | Agregar webhook_dispatch() en crear_potencial y move (lead.creado, lead.etapa_cambiada) |
3 |
| admin/php/configuracion_bd.php | Cifrar token DoWISP con phpencrypt() al guardar |
1 |
| admin/php/producto_bd.php | Agregar campo es_servicio en save/load |
2 |
| admin/php/familias_bd.php | Agregar campos margen_master_pct, margen_distrib_pct en save/load |
2 |
| admin/php/subfamilias_bd.php | Agregar campos margen_master_pct, margen_distrib_pct en save/load |
2 |
| admin/php/edistribuidor_bd.php | Agregar campos tipo_impuesto, porcentaje_impuesto, parent_uuid, org_tipo, observaciones_privadas en save/load |
2 |
| admin/php/contacts_bd.php | Agregar campo dowisp_cliente_id en save/load |
1 |
| admin/php/agentes_bd.php | Agregar campo rol (dropdown) |
2 |
| Archivo nuevo: | ||
| distribuidor/php/cobertura_bd.php | Nuevo: 3 funciones de busqueda cobertura (Sarenet CSV, Onivia API, CableMovil) | 3 |
Matriz completa de permisos. SI = tiene acceso. NO = no tiene acceso.
| Permiso | admin_super | director | distribuidor | agente |
|---|---|---|---|---|
* (wildcard) |
SI | NO | NO | NO |
pipeline.leer |
SI | SI | NO | NO |
pipeline.editar |
SI | SI | NO | NO |
actividad.leer |
SI | SI | SI | NO |
actividad.crear |
SI | SI | SI | NO |
stats.leer |
SI | SI | NO | NO |
dashboard.director |
SI | SI | NO | NO |
admin.config |
SI | SI | NO | NO |
distribuidor.leer |
SI | SI | SI | NO |
distribuidor.crear |
SI | SI | NO | NO |
distribuidor.editar |
SI | SI | NO | NO |
distribuidor.activar |
SI | SI | NO | NO |
| Acceso a paginas (implicito por rol) | ||||
| Presupuestos (listar/crear) | SI | SI | SI | SI |
| Documentos | SI | SI | SI | SI |
| Cobertura | SI | SI | SI | SI |
| Portal Admin | SI | SI | NO | NO |
Nota: admin_super tiene permiso wildcard * que otorga acceso a todo automaticamente. La tabla muestra permisos explicitos para los demas roles, que se configuran en config_roles.permisos como array JSON. Los valores mostrados para director y distribuidor son la configuracion recomendada; los valores reales dependen de lo que se haya insertado en la tabla config_roles.
Manual Tecnico del Presupuestador Conexia - Version 1.0 - Abril 2026
Conexia Telecomunicaciones | pedidos.conexiatec.com