docs / systema / v1.2
v1.2 PHP 7.4+ MySQL 8.x
Systema · Ayuda Oficial

¿Qué es Systema?

ERP de facturación y gestión comercial para freelancers y pequeñas empresas. Sin frameworks, sin dependencias complejas — PHP nativo que funciona en cualquier hosting compartido.

PHP 7.4+ / 8.x MySQL InnoDB · 15 tablas Sin Composer Multi-marca SMTP + Tracking AEAT M303 / M130 / M390 Resultados Anuales Portal contable trimestral Cron + ZIPs fiscales Zoom UI configurable Design System unificado Licencias multisite HMAC-SHA256 Auto-actualización VeriFactu Fase A ✓ Apps dinámicas Token anti-enumeración RGPD

Systema es un ERP ligero de gestión comercial pensado para autónomos, freelancers y pequeñas empresas que necesitan una herramienta de facturación potente sin la complejidad de ERPs empresariales.

Funciona con PHP nativo y MySQL — sin Composer en producción, sin frameworks, sin dependencias externas que gestionar. Se instala subiendo los archivos por FTP y configurando la base de datos desde el propio panel.

🧾 Facturación completa

Crea, envía y cobra facturas con tracking de apertura, generación de PDF y acceso público para el cliente via token seguro.

📋 Presupuestos

Genera presupuestos y conviértelos a factura en un click cuando el cliente los acepta.

🏛️ Presentador AEAT

Calcula y genera automáticamente los modelos M303, M130 y M390 desde los datos de facturación.

⚡ Status & Mantenimiento

Panel de diagnóstico completo del sistema — BD, índices, SMTP, PDF, PHP y disco en tiempo real.

📁 Resultados Anuales

Genera cierres fiscales anuales o trimestrales. Portal seguro para el contable con ZIPs de facturas, Excel y PDFs de modelos AEAT incluidos.

🖥️ Interfaz adaptable

Zoom configurable (70%-125%) guardado en servidor. Sin flash visual. Ideal para distintos tamaños de pantalla.

📦 Catálogo de productos

Módulo de productos a medida con categorías, márgenes por categoría y cálculo automático de PVP. BD lista, UI en v1.3.

🧩 Apps dinámicas

Crea /apps/{slug}/index.php y aparece automáticamente en el sidebar como módulo extra. Sin tocar el core. Módulos específicos por instalación.

🔐 VeriFactu Fase A

QR en cada factura vinculado a un token único por documento. Visor público sin login con token validado en servidor — imposible acceder a facturas ajenas. Fase B (envío AEAT) en agosto 2026.

Stack técnico

PHPMySQLVanilla JS
CapaTecnologíaVersiónNotas
BackendPHP nativo7.4+ / 8.xSin frameworks. PDO + prepared statements.
Base de datosMySQL InnoDB5.7+ / 8.xutf8mb4_unicode_ci. FKs con ON DELETE CASCADE/RESTRICT.
FrontendHTML + CSS + JS VanillaSin React ni Vue. Chart.js 4.4 para gráficas (CDN).
PDF (visor)HTML → print()Plantilla HTML personalizable con marcadores.
PDF (export)dompdf v2.0.42.0.4Requiere instalación aparte. Con Masterminds/html5-php 2.7.6.
PDF (AEAT)FPDF 1.86 + FPDI1.86 / masterRellena plantillas PDF oficiales de la AEAT.
EmailPHPMailer6.xSMTP con TLS/SSL. Incluido en /vendor/phpmailer/.
Excel exportxlsx-js-style1.2.0Generación de Excel con estilos desde el navegador. CDN jsdelivr.
AuthSesiones PHP nativasPassword único en config/settings.php.
ConfigFile-based PHPconfig/settings.php generado automáticamente. Nunca en git.

Arquitectura de módulos

Cada acción del sistema es un endpoint PHP independiente en /modules/. El frontend llama a estos endpoints con fetch() via JSON. No hay estado compartido entre peticiones — cada endpoint valida sesión, conecta a BD y responde.

POST /modules/facturacion/create.php  → { success, id, numero }
POST /modules/facturacion/send_email.php → { success, email }
GET  /modules/facturacion/pdf_export.php?id=5 → factura.pdf
GET  /modules/facturacion/pdf_render.php?token=XYZ → HTML público

Instalación

FTPphpMyAdmin
Subir archivos por FTPSube el contenido de systema/ al directorio deseado de tu servidor.
Acceder a la URLAbre tudominio.com/systema/ — redirige automáticamente a configuracion.php si no hay BD configurada.
Configurar base de datosIntroduce host, usuario, contraseña y nombre de BD. Systema crea todas las tablas desde systema_5.0.5.sql automáticamente (incluye tabla resultados_anuales y vistas de dashboard).
Instalar dompdf (opcional)Necesario para exportación PDF. Sube install_dompdf.php y install_mm.php, ejecútalos y bórralos. Alternativa: sube manualmente vendor/dompdf/ y vendor/masterminds/.
SQL completo v0.5.5Importa systema_5.0.5.sql — incluye tablas, índices, vistas y la nueva tabla resultados_anuales. Para instalaciones existentes en v0.4, ejecutar solo el ALTER de añadir la nueva tabla.
Configurar datos fiscales y SMTPDesde Configuración en el panel, rellena datos fiscales del emisor y credenciales SMTP para envío de facturas.
🔑
La contraseña de acceso está definida en index.php como constante SYSTEMA_PASSWORD. Cámbiala directamente en el archivo para mayor seguridad.

Recuperación de contraseña

La pantalla de login incluye el modal "He olvidado mi contraseña". Al introducir el email, reset_password.php consulta la base de datos central de licencias, genera una contraseña temporal segura con random_int(), la hashea con password_hash(), la guarda en config/settings.php y la envía por email al cliente. La respuesta siempre es genérica — no revela si el email existe o no.

Dashboard

Solo lectura

Pantalla principal del sistema. Ofrece una visión global del estado del negocio al momento de acceder.

KPIs en tiempo real

MétricaCálculo
Ingresos cobradosSuma de total de facturas con estado = 'cobrada'
Pendiente de cobroSuma de total de facturas con estado = 'enviada'
Facturas vencidasCount de facturas con estado = 'vencida'
Clientes activosCount total de la tabla clientes

Gráficos

Últimas facturas

Tabla con las 6 facturas más recientes. Click en cualquier fila abre el PDF en nueva pestaña.

Notificaciones de vencidas v0.5.5

Badge rojo estilo iOS en la campana del topbar con el número de facturas vencidas. Se actualiza automáticamente al cargar los KPIs. Click abre el modal de vencidas.

Modal de facturas vencidas v0.5.5

Lista todas las facturas con estado vencida del período seleccionado. Incluye dos acciones por fila:

Filtros del Dashboard v0.5.3

Selector global que controla todos los KPIs y gráficas: Histórico total, año concreto (2019–2026) o trimestre del año actual. Los subfiltros de cada gráfica se adaptan dinámicamente según el global seleccionado.

Filtro globalSubfiltros disponiblesDefault
Histórico totalSolo "Histórico"Histórico
Año pasadoAño, HistóricoAño
Año actualSemana, Mes, Año, HistóricoMes
TrimestreSemana, MesMes

Acceso rápido v0.6.0

Botones de acción rápida en el Dashboard: Nueva Factura y Nuevo Presupuesto. Navegan directamente al módulo correspondiente y abren el modal de creación automáticamente via parámetro ?nuevo=1 en la URL.

Leads

CRM básicov0.6.0

Pipeline comercial para gestionar contactos antes de que se conviertan en clientes. Permite seguir el estado de cada oportunidad y convertirla a cliente cuando está lista.

Estados del pipeline

EstadoSignificado
nuevoLead recién creado, sin contacto todavía
contactadoSe ha establecido contacto inicial
cualificadoHay oportunidad real de negocio
perdidoEl lead no va a convertirse
convertidoSe creó un cliente a partir de este lead

Convertir lead a cliente

La acción Convertir crea automáticamente un cliente con los datos del lead (nombre, email, teléfono) y marca el lead como convertido. Verifica que no exista ya un cliente con el mismo email antes de crear.

Rediseño completo v0.6.0

El módulo recibió un rediseño completo con multiselect, bulk actions y exportación:

Clientes

Completo v0.3v0.6.0

Base de datos de clientes y empresas. Cada cliente tiene ficha completa con datos fiscales, datos de contacto, forma de pago e IBAN para transferencias.

Campos disponibles

CampoDescripción
tipoempresa / particular
nombreNombre comercial — aparece en listados
nombre_fiscalRazón social completa para facturas
nif_cifNIF/CIF para datos fiscales en factura
emailEmail de contacto general
email_facturacionEmail específico para envío de facturas. Tiene prioridad sobre email.
forma_pagoTexto libre: "Transferencia 30 días", etc.
ibanIBAN para datos de pago en facturas
activov0.6.0 TINYINT(1). Toggle en fila — activo (verde) / inactivo (gris). Filtra visibilidad en listados.
notasNotas internas — no aparecen en facturas

Sistema Activo / Inactivo v0.6.0

Toggle por fila para marcar clientes como activos o inactivos. Disponible también en bulk actions (activar/desactivar seleccionados) y en la toolbar de filtrado. Los clientes inactivos no aparecen en los selectores de nueva factura/presupuesto.

Importación masiva desde Excel

Arrastra o selecciona un archivo .xlsx — el sistema lo parsea en el navegador con SheetJS y muestra una preview de 5 filas antes de importar. La plantilla Excel descargable está en /modules/clientes/clientes.xlsx.

Exportación XLSX v0.6.0

Exporta todos los clientes a .xlsx con cabecera profesional y estilos via SheetJS (antes exportaba a CSV). Compatible con Excel español sin problemas de codificación.

Presupuestos

Fix v0.4Completo v0.6.0

Crea presupuestos detallados con líneas de concepto y envíalos al cliente. Cuando el cliente acepta, conviértelo a factura en un click.

Flujo completo

1
Crear presupuestoSelecciona cliente, marca, añade líneas con descripción, cantidad y precio. El sistema calcula base, IVA, IRPF y total automáticamente.
2
Enviar al clienteEmail con PDF adjunto via PHPMailer + pixel de tracking. El cliente recibe su presupuesto con enlace de acceso público por token.
3
Chip "Leído"Cuando el cliente abre el email, el sistema registra la apertura. La tabla muestra el chip Leído con la fecha y hora.
4
Cambiar a aceptadoCuando el cliente confirma, marca el presupuesto como aceptado. Solo los presupuestos en este estado pueden convertirse.
5
Convertir a facturaEl sistema genera automáticamente una factura nueva con el siguiente número correlativo, copia todas las líneas y marca el presupuesto como convertido con referencia a la factura creada.

Estados

EstadoDescripciónPuede convertir
borradorEn preparaciónNo
enviadoEnviado al clienteNo
aceptadoCliente confirma✅ Sí
rechazadoCliente no aceptaNo
caducadoPasó la fecha de validezNo
convertidoYa se generó facturaNo

Paridad con facturación v0.6.0

El módulo de presupuestos alcanzó paridad completa con facturación. Endpoints disponibles:

EndpointFunción
modules/presupuestos/create.phpPOST — crea presupuesto + snapshot emisor
modules/presupuestos/update.phpPOST — edita estado, cliente, líneas
modules/presupuestos/delete.phpPOST — elimina individual o bulk
modules/presupuestos/convert.phpPOST — convierte a factura
modules/presupuestos/send_email.phpPOST — envía por email con PHPMailer + tracking
modules/presupuestos/pdf_export.phpGET — genera PDF con dompdf
modules/presupuestos/pdf_render.phpGET — visor HTML público por token
modules/presupuestos/duplicate.phpPOST — duplica presupuesto
modules/presupuestos/get_list.phpPOST — lista con filtros, paginación y stats
modules/presupuestos/bulk_estado.phpPOST — bulk: marcar aceptado, rechazado
modules/presupuestos/bulk_delete.phpPOST — bulk: eliminar seleccionados
Arreglado en v0.4: Los módulos create.php, convert.php y delete.php tenían referencias a tablas y columnas incorrectas del schema antiguo que causaban error SQL. Corregido completamente.

Facturación

Completo v0.5.5Core del sistema

Módulo principal de Systema. Gestiona el ciclo de vida completo de cada factura desde su creación hasta el cobro, incluyendo envío por email con tracking de apertura.

Estados de factura

EstadoDescripción
borradorCreada pero no enviada al cliente
enviadaEmail enviado al cliente. Entra en "pendiente de cobro".
leídav0.6.0 El cliente abrió el email. Chip visible en la tabla con timestamp.
cobradaPago recibido. Entra en "ingresos cobrados".
vencidaPasó la fecha de vencimiento sin cobrar.

Numeración automática

Cada factura recibe un número correlativo único por marca. El formato es configurable en marcas.prefijo_factura con soporte para el token {YEAR}. Ejemplo: RG{YEAR}- genera RG2026-0001.

El contador se actualiza con FOR UPDATE en una transacción MySQL para evitar números duplicados en caso de peticiones concurrentes.

Pipeline de envío por email

1
Generar token públicoSe crea o recupera un public_token de 48 caracteres hex único por factura. Este token permite al cliente ver la factura desde el email sin necesidad de login.
2
Construir HTML del emailEmail responsive table-based con: cabecera del emisor, datos del cliente, tabla de líneas, totales, datos de pago (IBAN, forma de pago) y notas al cliente.
3
Pixel de trackingSe genera un tracking_id de 32 hex chars y se inyecta como imagen 1×1 px en el email. Cuando el cliente abre el email, el servidor registra IP, user agent y timestamp.
4
Envío SMTP con PHPMailerSe envía con las credenciales SMTP configuradas (TLS/SSL/none). Si la factura estaba en borrador, pasa automáticamente a estado enviada.
5
Registro en BDSe actualizan facturas.email_enviado, email_enviado_at y email_tracking_id. Se crea entrada en la tabla email_tracking.

Visor de factura (pdf_render.php)

Renderiza la factura como HTML en el navegador usando la plantilla personalizable. Admite dos modos de acceso:

Exportación PDF (pdf_export.php)

Genera PDFs con dompdf v2 usando HTML interno optimizado para el renderizador.

⚠️
Plantilla PDF: pdf_render.php usa la plantilla HTML personalizable del usuario. pdf_export.php usa un HTML interno simplificado para garantizar compatibilidad con dompdf — el resultado es funcional pero más austero visualmente.

Tracking de apertura

Cuando el cliente abre el email y carga la imagen 1×1, track_open.php registra la apertura. Usa COALESCE(abierto_at, NOW()) para guardar solo la primera apertura, ignorando las siguientes. El estado visual de la factura cambia a "leída" en el listado.

Filtros y buscador v0.5.4

La carga inicial filtra solo el año actual via BETWEEN en lugar de YEAR() para aprovechar los índices. El año se persiste en localStorage con validación contra los años reales de la BD para evitar bucles infinitos de redirección.

URL ?buscar=FAC-00123 — navegar con este parámetro desde el dashboard carga todas las facturas, inyecta el número en el buscador y filtra automáticamente.

Buscador de cliente v0.5.4

El modal de nueva/editar factura usa un campo de búsqueda con dropdown en lugar del <select> nativo. Filtra por nombre y NIF simultáneamente, resalta coincidencias. El dropdown se renderiza en el body con position:fixed para evitar el overflow del modal.

Stats bar v0.5.4

Barra de resumen que aparece cuando hay cualquier filtro activo. Muestra: N resultados (tungsteno), facturado, desglose por estado y última factura (todo en gris). Los KPIs superiores se recalculan en tiempo real al filtrar.

Exportación Excel v0.5.4

Botón "Exportar" en el topbar genera un Excel con xlsx-js-style con cabecera profesional: título, fecha de exportación, período calculado automáticamente, datos fiscales del emisor y dos filas de totales (cobrado en verde + total general en azul). Siempre ordenado cronológicamente ascendente.

Snapshot del emisor v0.6.0

Al crear una factura, modules/facturacion/create.php lee los datos fiscales actuales de configuracion (grupo fiscal) y los guarda como snapshot en los campos emisor_nombre, emisor_nif y emisor_direccion de la tabla facturas. Aplica igualmente a presupuestos.

⚖️
Relevancia legal: Sin este fix, cambiar la razón social en Configuración modificaba retroactivamente todas las facturas antiguas al regenerar el PDF. Ahora cada factura conserva los datos del emisor del momento exacto de su emisión.

Resultados Anuales

Nuevo v0.5.5Trimestral v0.6.0Fiscal

Módulo para generar, gestionar y enviar los cierres fiscales — anuales o trimestrales — al gestor o contable. Automatiza la generación de ZIPs con todas las facturas del período organizadas por trimestre, y proporciona un portal seguro de descarga sin necesidad de login.

Flujo completo

1
Crear resultadoSelecciona ejercicio (año), período (Anual / T1 / T2 / T3 / T4), modelos AEAT a incluir (M303, M130, M390), email y nombre del contable. Al crear, el sistema lanza automáticamente la generación en background con spinner en el botón.
2
Generación en backgroundmodules/resultados/generate.php genera los PDFs de las facturas del período con dompdf, los agrupa en ZIPs por trimestre y los guarda en assets/resultados/{año}/. Estado en tiempo real: ProcesandoPreparado.
3
Enviar al contableSolo disponible cuando el estado es "Preparado". Envía un email profesional con enlace al portal seguro vía PHPMailer y las credenciales SMTP configuradas.
4
Portal del contableEl contable accede a resultado_publico.php?token=XYZ — sin login. Ve el desglose de facturas del período, importes y botones de descarga ZIP. Si se incluyeron modelos AEAT, aparecen botones morados para descargar cada PDF directamente. Los ZIPs caducan a los 14 días.

Modo trimestral v0.6.0

Al crear un resultado, el selector de período permite elegir entre Anual (ejercicio completo, 4 ZIPs) o un trimestre específico (T1/T2/T3/T4, 1 ZIP). La BD filtra automáticamente las facturas del rango de fechas correcto. El portal público se adapta mostrando solo el trimestre relevante.

Modelos AEAT en el ZIP v0.6.0

Al crear un resultado se pueden marcar los modelos AEAT a incluir. M303 y M130 están marcados por defecto; M390 solo se activa para Anual o T4. Los PDFs de los modelos se sirven desde modules/presentador/pdf_publico.php — endpoint sin sesión que valida el token del resultado antes de servir el PDF.

ModeloDisponible enDefault
M303 — IVA trimestralTodos los períodos✅ Marcado
M130 — IRPF fraccionadoTodos los períodos✅ Marcado
M390 — Resumen anual IVASolo Anual y T4— Desmarcado

Tabla de resultados

Muestra una tabla comparativa con el desglose por ejercicio: facturas totales, cobrado, pendiente, vencidas, borradores, total emitido y barra de progreso de % cobrado. La columna Ejercicio muestra el trimestre bajo el año cuando el resultado no es anual.

Estados del ZIP

EstadoDescripción
Procesandogenerate.php está generando PDFs y ZIPs en background
PreparadoZIPs generados en disco. Botón Enviar activo.
ErrorFallo durante la generación. Revisar plantilla PDF y logs.
CaducadoHan pasado 14 días. Los archivos fueron eliminados por el cron.

Archivos del módulo

ArchivoFunción
resultados.phpPágina principal. Lista, stats, modal de creación con selector período y checkboxes AEAT.
resultado_publico.phpPortal público del contable. Acceso por token, sin sesión. Soporte trimestral + botones PDF AEAT.
modules/resultados/create.phpPOST: Crea registro en BD con periodo y modelos_aeat. Lanza generate.php en background.
modules/resultados/generate.phpGET interno: Genera PDFs con dompdf y ZIPs por trimestre. Actualiza zip_status en BD.
modules/resultados/update.phpPOST: Edita email_contable, nombre_contable, observaciones y estado.
modules/resultados/send.phpPOST: Envía email profesional al contable con enlace al portal. Actualiza estado a 'enviado'.
modules/resultados/status.phpPOST: Polling del estado zip_status para actualización en tiempo real (polling cada 5s).
modules/resultados/cleanup.phpCLI/Cron: Elimina ZIPs caducados (+14 días) y resetea zip_status para permitir regeneración.
modules/resultados/get_facturas.phpPOST: Devuelve facturas del ejercicio para export Excel desde el módulo.
modules/presentador/pdf_publico.phpNuevo v0.6.0 Sirve PDFs de modelos AEAT sin sesión, autenticado por token del resultado.

Schema BD: resultados_anuales

CampoTipoDescripción
idINT AUTO_INCREMENTPK
ejercicioYEAR NOT NULLAño fiscal del cierre
periodoENUM(anual,T1,T2,T3,T4) DEFAULT anualv0.6.0 Anual o trimestre específico
modelos_aeatVARCHAR(30) NULLv0.6.0 Modelos incluidos. Ej: M303,M130
email_contableVARCHAR(255)Email del gestor
nombre_contableVARCHAR(150)Nombre del gestor para personalizar el email
observacionesTEXTNotas internas del usuario
estadoENUM(pendiente,enviado)Estado de envío al contable
zip_statusENUM(procesando,preparado,error)Estado de generación del ZIP
zip_generado_atDATETIMETimestamp de generación
zip_expires_atDATETIMECaducidad del ZIP (generado_at + 14 días)
enviado_atDATETIMETimestamp de envío del email
email_tracking_idVARCHAR(64) NULLv0.6.0 UUID pixel tracking apertura email
email_abiertoTINYINT(1) DEFAULT 0v0.6.0 Flag apertura email por el contable
email_abierto_atDATETIME NULLv0.6.0 Timestamp de apertura
tokenVARCHAR(64) UNIQUEToken hex de 64 chars para URL pública
created_atDATETIME DEFAULT NOW()Creación del registro
🗄️
Migraciones aplicadas en producción: ALTER TABLE resultados_anuales ADD COLUMN periodo y ADD COLUMN modelos_aeat (v0.6.0). Columnas de tracking email añadidas en v0.6.0. Charset homogeneizado a utf8mb4 y 3 índices restaurados en v0.6.0.

Zoom de Interfaz UI

Nuevo v0.5.5

Sistema de zoom configurable para adaptar la interfaz al tamaño de pantalla del usuario. El zoom se guarda de forma persistente en el servidor (config/settings.php) y en el navegador (localStorage), de modo que sobrevive a limpiezas de caché.

Configuración

Disponible en Configuración → Interfaz UI. Valores disponibles: 70%, 75%, 80%, 85%, 90%, 95%, 100%, 110%, 115%, 120%, 125%.

PantallaZoom recomendado
Portátil 13" / 14"80%
Portátil 15" / 16"85% – 90%
Portátil 17"90% – 100%
Monitor 24"100% – 110%
Monitor 27" / 32"110% – 120%
Pantalla 4K grande120% – 125%

Implementación técnica

Presentador AEAT

AutomáticoEspaña

Módulo que calcula automáticamente los valores de los modelos tributarios de la AEAT a partir de los datos de facturación almacenados en la BD. No necesitas introducir nada manualmente — Systema lee tus facturas y calcula las casillas.

🤖
Automático: Selecciona el modelo, el año y el período (trimestre o anual). Systema consulta la BD, agrupa las facturas por tipo de IVA y calcula todas las casillas del modelo en tiempo real.

Modelos soportados

M303 — IVA Trimestral

Declaración trimestral de IVA. Casillas 01-09 (bases y cuotas al 21%, 10% y 4%), C27 (total devengado), C46 (resultado a ingresar/devolver).

M130 — IRPF Fraccionado

Pagos fraccionados del IRPF. C01 ingresos, C03 rendimiento neto, C04 (20% de C03), C05 retenciones soportadas, C06 resultado.

M390 — Resumen Anual IVA

Resumen anual de IVA. Casillas anuales C01-C09 por tipo de gravamen, C95 volumen total de operaciones.

Filtros disponibles

Generación de PDF oficial

Usa FPDI para importar las plantillas PDF oficiales de la AEAT (m303.pdf, m130.pdf, m390.pdf) y FPDF para escribir los valores calculados en las coordenadas exactas de cada casilla. El resultado es un PDF rellenado listo para presentar.

Historial de declaraciones

Cada presentación queda guardada en la tabla presentaciones_historial con el snapshot JSON completo de los datos calculados, permitiendo re-descargar cualquier declaración anterior sin recalcular.

Configuración

9 secciones
SecciónQué configuraGuardado en
Base de datosHost, puerto, usuario, contraseña, nombre de BD. Test de conexión incluido.config/settings.php
Datos fiscalesRazón social, NIF, dirección, CP, ciudad, provincia, teléfono, email, IBAN del emisor.configuracion (grupo: fiscal)
MarcasMúltiples marcas/empresas bajo un mismo NIF. Cada marca tiene prefijo de facturación y color.Tabla marcas
Email SMTPHost, puerto, usuario, contraseña, cifrado (TLS/SSL/none), nombre, email del remitente y Reply-To global.configuracion (grupo: smtp)
Plantilla PDFHTML completo de la factura con marcadores {{variable}}. Preview en tiempo real.configuracion (grupo: plantilla)
NumeraciónPrefijos y padding de correlativo para facturas y presupuestos. Soporte para {YEAR}.configuracion (grupo: numeracion)
Seguridadv0.6.0 Contraseña del portal contable (portal_password). Protege resultado_publico.php.configuracion (grupo: seguridad)
IA / GroqAPI key de Groq, modelo LLM y activación. Fase 2configuracion (grupo: ia)
VeriFactuEndpoint y API key. Próximamenteconfiguracion (grupo: verifactu)

Reply-To SMTP v0.6.0

Campo reply_to opcional en la configuración SMTP. Cuando está definido, todos los emails enviados desde Systema (facturas, presupuestos, resultados) incluyen la cabecera Reply-To apuntando a esa dirección. Útil para redirigir respuestas de clientes a una bandeja específica.

Seguridad del portal contable v0.6.0

El campo portal_password (grupo seguridad) protege el acceso al portal público del contable con una pantalla de login. Si está vacío, el portal es de acceso libre por token.

Nuevos endpoints v0.6.0

EndpointFunción
modules/configuracion/save_seguridad.phpPOST — guarda portal_password en grupo seguridad
modules/configuracion/save_acceso.phpPOST — guarda configuración de acceso y sesión
modules/configuracion/save_php.phpPOST — diagnóstico y ajustes de entorno PHP

Marcadores de plantilla PDF

La plantilla HTML acepta los siguientes marcadores que se sustituyen con los datos reales de cada factura:

{{numero}}           {{fecha_emision}}      {{fecha_vencimiento}}
{{cliente_nombre}}   {{cliente_nif}}        {{cliente_email}}
{{cliente_direccion}}{{cliente_cp}}         {{cliente_ciudad}}
{{emisor_nombre}}    {{emisor_nif}}         {{emisor_direccion}}
{{emisor_email}}     {{emisor_telefono}}    {{emisor_iban}}
{{emisor_forma_pago}}{{base_imponible}}     {{iva_porcentaje}}
{{iva_importe}}      {{irpf_porcentaje}}    {{irpf_importe}}
{{total}}            {{notas_cliente}}      {{lineas}}

Eliminar marcas

Desde Configuración → Marcas, cada marca dispone de un botón de eliminar (icono papelera). El endpoint modules/configuracion/delete_marca.php verifica antes de borrar que la marca no tenga facturas ni presupuestos vinculados. Si los tiene, devuelve un error descriptivo y la operación se cancela. Si no tiene documentos, la elimina de la tabla marcas.

Panel de Status & Mantenimiento

Nuevo v0.4Diagnóstico completo

Panel de control técnico del sistema. Accesible desde el sidebar en status.php. Monitoriza en tiempo real el estado de todos los componentes críticos de Systema.

🟢
Regla de oro: Si el chip global de arriba está verde con "TODO NOMINAL", no necesitas mirar nada más. El sistema está funcionando perfectamente.

Chip de estado global

Lo primero que ves al entrar. Barra a todo el ancho que resume el estado de todo el sistema en un único indicador:

ColorEstadoQué significa
● VERDETODO NOMINALTodos los sistemas operativos. No hay nada que hacer.
● NARANJAATENCIÓN REQUERIDAAlgo a vigilar pero el sistema funciona. El detalle indica qué.
● ROJOSISTEMA CON ERRORESHay un problema real que impide el funcionamiento correcto.

Sectores A–J explicados

SECTOR A · DB Engine
Solo lectura

Muestra el estado de la conexión MySQL: versión del servidor, tiempo en marcha (uptime), conexiones activas, queries lentas y total de queries ejecutadas desde que arrancó el servidor.

Queries lentas > 5: el chip pasa a naranja. Indica que algunas queries tardan más de lo esperado — puede ser señal de que los índices necesitan revisión.

SECTOR B · Index Integrity
Solo lectura

Verifica uno a uno los 20+ índices del sistema con SHOW INDEX FROM tabla. LED verde = índice presente y activo. LED rojo = índice faltante — el sistema funciona pero más lento.

¿Cuándo puede ponerse rojo? Si se restauró la BD desde un backup anterior a v0.4, o si hubo un fallo durante la migración de índices.

SECTOR C · Table Sizes
Solo lectura

Consulta information_schema.TABLES para mostrar el número de filas y el tamaño en KB (datos + índices) de cada tabla. Útil para entender cómo crece la BD con el tiempo.

SECTOR D · SMTP Stack
Diagnóstico

Muestra la configuración SMTP activa y el estado del tracking de emails. Si el campo Config Status dice NOT CONFIGURED, los envíos de facturas fallarán.

Tracking pendiente: número de emails enviados cuyo pixel no ha sido cargado todavía por el cliente (el cliente no ha abierto el email).

SECTOR E · PDF Engine
Diagnóstico

Verifica que todas las librerías de PDF estén instaladas. FPDF es obligatoria (AEAT). dompdf + Masterminds son necesarias para exportación PDF de facturas — si no están, solo funciona el visor HTML.

También verifica que /tmp sea escribible — necesario para generar ZIPs de PDFs en bulk.

SECTOR F · AEAT Presentador
Diagnóstico

Verifica que las 3 plantillas PDF oficiales de la AEAT estén en /presentador/templates/. Si alguna falta, el modelo correspondiente no podrá generar el PDF oficial rellenado.

SECTOR G · PHP Runtime
Solo lectura

Versión PHP, compatibilidad con 7.4+, límites de memoria y tiempos de ejecución. Si el chip está rojo, PHP es incompatible o hay un problema de configuración del servidor.

SECTOR H · Extensions
Solo lectura

Extensiones PHP críticas: pdo_mysql (BD), openssl (SMTP TLS), mbstring (textos), json (API interna), zip (bulk PDF). Si alguna falta en rojo, esa funcionalidad no operará.

SECTOR I · Disk & Storage
Informativo

En hosting compartido, disk_free_space() devuelve 0 porque el servidor no expone esta información a PHP. Esto es completamente normal — el sector muestra "N/D (hosting compartido)" y permanece en verde si /tmp es escribible y config/settings.php existe.

Los datos de Systema van a MySQL, no al disco — el espacio de hosting no es un factor crítico.

SECTOR J · Core File Manifest v0.6.0
Integridad

Verifica la presencia e integridad de los 94 archivos core de Systema. Cada archivo se compara contra el manifest SHA256 incluido en el ZIP de distribución. Un chip verde confirma que ningún archivo core ha sido modificado o eliminado accidentalmente. Chip rojo indica archivos faltantes o alterados — útil tras actualizaciones manuales por FTP.

Acciones y niveles de riesgo

AcciónRiesgoQué haceCuándo usarla
ANALYZE TABLES Seguro Actualiza estadísticas internas de MySQL para que use los índices de forma más eficiente. Tras importaciones masivas de datos.
SMTP HANDSHAKE Seguro Abre conexión TCP al servidor SMTP y lee el banner. Solo comprueba conectividad, no autentica. Para verificar que el host SMTP es alcanzable.
PURGE TEMP FILES Seguro Borra archivos /tmp/sys_fac_* y /tmp/dompdf_* — residuos de PDFs generados. Si Temp files muestra un número alto.
OPTIMIZE TABLES Bloquea Compacta y reorganiza el almacenamiento interno de InnoDB. Bloquea las tablas unos segundos. Con el sistema sin usuarios activos, una vez al mes.
TEST SEND EMAIL Envía Manda un email de prueba real a la dirección email_from configurada en SMTP. Para verificar que el envío de facturas funcionará correctamente.
FLUSH QUEUE Borra Elimina logs de email_tracking de emails enviados hace más de 30 días que nunca fueron abiertos. Limpieza periódica del historial de tracking.
REINDEX ALL v0.4 Peligroso Borra y recrea todos los índices de rendimiento v0.4. Las tablas quedan temporalmente sin índices. Solo si Sector B muestra índices en rojo. Siempre con el sistema sin usuarios activos.

Índices de rendimiento v0.4

Migración aplicada en v0.4 para soportar volúmenes de 10.000+ facturas sin degradación. Se aplica ejecutando systema_v04_indexes.sql en phpMyAdmin.

idx_facturas_created_at
idx_facturas_public_token
idx_facturas_estado_fecha
idx_facturas_marca_fecha
idx_presupuestos_created_at
idx_presupuestos_fecha
idx_presupuestos_estado_fecha
idx_tracking_doc_open
idx_leads_estado
idx_leads_created_at
ÍndiceTablaPropósito
idx_facturas_created_atfacturasORDER BY en el listado principal — sin él MySQL ordena 10k filas en memoria
idx_facturas_public_tokenfacturasWHERE public_token en pdf_render.php — lookup del cliente desde email
idx_facturas_estado_fechafacturasCompuesto (estado, fecha) — Presentador AEAT y KPIs del dashboard
idx_facturas_marca_fechafacturasCompuesto (marca_id, fecha) — Presentador AEAT filtrado por marca
idx_presupuestos_fechapresupuestosCOMPLETAMENTE AUSENTE antes de v0.4 — M390 anual hace WHERE BETWEEN fecha
idx_tracking_doc_openemail_trackingCompuesto 3 columnas — reemplaza idx_tracking_doc añadiendo abierto_at para index-only scan en la subquery correlated del listado (1 subquery por fila)

Schema de base de datos

InnoDB · utf8mb4
TablaRegistros actualesDescripción
marcas~pocasMarcas o empresas del emisor. Cada una tiene prefijo de numeración, color y contador de siguiente número.
clientescrecienteFicha completa de clientes. Campo activo añadido en v0.6.0. Índices en nombre, NIF y email.
facturascrecienteCabecera de cada factura. Incluye snapshot del emisor (v0.6.0), tracking de email y token público.
factura_items~4x facturasLíneas de detalle. ON DELETE CASCADE al borrar factura.
presupuestoscrecienteIdéntica a facturas con estados adicionales y referencia a factura generada. Snapshot emisor desde v0.6.0.
presupuesto_items~4x presupuestosLíneas de presupuesto. ON DELETE CASCADE.
leadscrecientePipeline comercial. FK opcional a clientes al convertir.
configuracion~50 fijasKey-value store de toda la configuración. Grupos: general, fiscal, smtp, numeracion, plantilla, seguridad, ia, verifactu.
email_tracking~1 por emailLog de aperturas de email. abierto_at = NULL hasta que el cliente abre el email.
resultados_anualespor cierreCierres fiscales. 16 campos incluyendo periodo, modelos_aeat y tracking email contable (v0.6.0).
presentaciones_historialpor presentaciónHistorial de modelos AEAT generados con snapshot JSON completo.
series_numeracionvacía por ahoraPreparada para numeración avanzada por series. Actualmente los contadores están en marcas.
producto_categoriasvacíaNueva v1.0 Categorías del catálogo de productos. Cada categoría tiene nombre y margen porcentual.
productosvacíaNueva v1.0 Catálogo maestro de productos. SKU único, coste neto, FK a categoría, proveedor y flag activo.

Vistas SQL

Módulo Productos

v1.0 — A medidaBD lista

Catálogo maestro de productos y servicios para agilizar la creación de facturas y presupuestos. La base de datos está creada y migrada en producción — la UI (autocomplete en modales de factura/presupuesto) está pendiente para v1.0.

📦
Estado actual: BD lista con 2 tablas nuevas. Plantilla Excel de importación masiva disponible en systema_productos_plantilla.xlsx. Endpoint modules/productos/search.php y JS autocomplete pendientes de implementación en v1.0.

Tablas de BD

TablaCampos claveDescripción
producto_categoriasid, nombre, margen_pct DECIMAL(5,2), activaCategorías del catálogo. El margen porcentual por categoría se usa para calcular el PVP automáticamente.
productosid, sku UNIQUE, descripcion VARCHAR(500), unidad DEFAULT 'ud', coste_neto DECIMAL(12,4), categoria_id FK, proveedor, activoCatálogo maestro. El PVP se calcula en runtime: coste_neto × (1 + margen_pct/100).

Lógica de PVP

El PVP no se almacena en BD — se calcula al vuelo: pvp = coste_neto × (1 + categoria.margen_pct / 100). Cambiar el margen de una categoría actualiza automáticamente el PVP de todos sus productos sin necesidad de UPDATE masivo.

Importación masiva

El archivo systema_productos_plantilla.xlsx incluye 4 pestañas: instrucciones, categorias, productos y SQL de referencia. Compatible con el formato de importación habitual de proveedores tipo Saltoki.

Licencias multisite

Nuevo v0.5.1HMAC-SHA256

Sistema de licencias para distribuir Systema a múltiples clientes. Cada instalación valida su licencia contra el servidor de licencias del autor mediante HMAC-SHA256 — sin enviar datos sensibles.

Funcionamiento

1
ActivaciónEl administrador introduce la clave de licencia en Configuración. Systema envía el dominio y la clave al servidor de activación remoto.
2
Validación HMAC-SHA256El servidor genera un token firmado con la clave secreta. La instalación local verifica la firma — sin comparación de strings planos, sin envío de la clave maestra.
3
Caché localEl resultado de la validación se cachea durante 7 días en config/. Las llamadas al servidor de licencias no ocurren en cada request — solo al expirar la caché o al forzar revalidación.

Dominios exentos

Los dominios localhost, rafelgil.com y entornos de desarrollo están exentos de validación para facilitar el desarrollo local sin necesidad de licencia activa.

Login Landing — Acceso desde systemafacil.com

Los clientes cloud acceden desde systemafacil.com introduciendo su email. El sistema LOGIN LANDING/login.php consulta lookup.php, que busca la url_instalacion en la BD central de licencias y redirige automáticamente al cliente a su instalación. No requiere contraseña en este paso — solo hace routing. La autenticación ocurre en la instalación destino.

Update Engine

Nuevo v0.5.6SHA256

Motor de actualizaciones automático que permite distribuir nuevas versiones de Systema a instalaciones en producción sin intervención manual por FTP. El proceso completo dura menos de 30 segundos.

Flujo de actualización

1
Detección de versiónEl panel de Status compara la versión instalada contra el servidor de distribución. Si hay versión nueva, aparece el botón "Actualizar".
2
Descarga y verificaciónDescarga el ZIP del parche desde el servidor. Verifica el SHA256 del archivo contra el manifest publicado — si no coincide, el proceso se aborta.
3
Backup automáticoAntes de aplicar cualquier cambio, el motor hace backup de los archivos core que van a ser sobreescritos en backups/pre-update-{fecha}/.
4
Aplicación del parcheSobreescribe los archivos del ZIP en el directorio raíz. Nunca toca config/settings.php, assets/resultados/ ni los datos de BD.
5
RollbackSi algo falla, el botón Rollback restaura los archivos del backup pre-update automáticamente.

Generador Core ZIP

Herramienta de desarrollo (actualizaciones.php) que genera automáticamente el ZIP de distribución con los 94 archivos core, el manifest SHA256 y los metadatos de versión. Solo accesible con credenciales de autor.

Asistente IA — Groq

Fase 2En desarrollo
🤖
Próximamente en Fase 2. La infraestructura ya está preparada — el endpoint existe, las claves están en la BD y los campos origen en facturas y presupuestos ya admiten los valores ia_voz e ia_chat.

¿Qué hará?

El asistente IA permitirá crear facturas y presupuestos mediante lenguaje natural, tanto por texto como por voz. Ejemplos de instrucciones:

"Crea una factura para Rafel Gil por diseño web, 1.500€, con 21% de IVA y 15% de IRPF"
"Duplica la última factura de este mes para el mismo cliente"
"¿Cuánto tengo pendiente de cobro este trimestre?"

Arquitectura técnica

Configuración necesaria

En Configuración → IA / Groq: introduce tu API key de Groq y activa el módulo con el toggle ia_activa.

VeriFactu

PróximamenteObligatorio AEAT
⚠️
VeriFactu será obligatorio en España para autónomos y empresas en los próximos años. Systema ya tiene la estructura preparada para integrarlo cuando sea necesario.

¿Qué es VeriFactu?

Sistema de verificación de facturas de la AEAT que exige que cada factura emitida sea registrada en tiempo real en los servidores de Hacienda con una firma digital. Garantiza la integridad e imposibilidad de modificación de facturas una vez emitidas.

Estado actual en Systema

Cron de mantenimiento

Nuevo v0.5.5Requiere cPanel/Plesk

Systema incluye un script de limpieza automática que se ejecuta vía cron job. Elimina los ZIPs de resultados generados que han superado los 14 días de validez y resetea su estado para permitir regeneración.

Configuración en Plesk / cPanel

# Ejecutar cada día a las 3:00 AM
0 3 * * * /usr/bin/php -f /var/www/vhosts/DOMINIO/PATH/systema/modules/resultados/cleanup.php

¿Qué hace el cron?

ℹ️
El portal del contable (resultado_publico.php) detecta automáticamente si los ZIPs han caducado y muestra el mensaje "No disponible" en lugar del botón de descarga. El usuario puede regenerar desde resultados.php.

Changelog

v0.6.0 Actual 20 de marzo de 2026
📅 Resultados — modo trimestral + modelos AEAT
Selector Anual / T1 / T2 / T3 / T4 en modal de creación de resultados. El portal contable filtra facturas y ZIPs al período seleccionado.
Checkboxes de modelos AEAT (M303, M130, M390) al crear resultado. M390 solo disponible en Anual o T4. Los PDFs aparecen como botones morados en el portal del contable.
Nuevo endpoint modules/presentador/pdf_publico.php — sirve PDFs de modelos AEAT sin sesión, autenticado por token del resultado. URL dinámica sin paths hardcodeados.
Migraciones BD: ADD COLUMN periodo ENUM y ADD COLUMN modelos_aeat VARCHAR(30) en resultados_anuales.
Spinner animado en botón "Crear resultado" mientras se genera en background. Dropdown ZIP con click (no hover) y z-index 9999.
⚖️ Fix legal: snapshot del emisor
modules/facturacion/create.php y modules/presupuestos/create.php — guardan snapshot de emisor_nombre, emisor_nif y emisor_direccion al crear. Cambiar datos fiscales ya no afecta retroactivamente a documentos existentes.
Fix concepto_global en export Excel del portal contable — campo referenciado como concepto (incorrecto) → concepto_global.

v0.5.8 19 de marzo de 2026
🎨 Design System unificado
Revisión completa de colores semánticos, tipografía y espaciados. Lenguaje de color coherente en todos los módulos — verde = éxito/cobrado, rojo = error/vencido, azul = enviado/pendiente, gris = borrador/inactivo.
👥 Leads — rediseño completo
Multiselect con checkbox por fila. Bulk: eliminar, convertir a cliente.
Exportar Excel con SheetJS. KPIs: Total · Valor activo · Cualificados · Convertidos.
👤 Clientes — activo/inactivo
Campo activo TINYINT(1) en tabla clientes. Toggle por fila, bulk y filtro en toolbar. Inactivos excluidos de selectores de nueva factura/presupuesto.
Export XLSX nativo con SheetJS — reemplaza CSV anterior.
📋 Presupuestos — paridad con facturación
PDF con dompdf, email con PHPMailer, tracking pixel, token público — paridad completa con el módulo de facturación. 11 endpoints.
Chip "Leído" cuando el cliente abre el email del presupuesto. Duplicar presupuesto.
🧾 Facturación
Chip "Leída" en tabla de facturas cuando el cliente ha abierto el email.
🔒 Seguridad portal contable
portal_password en Configuración → Seguridad. Protege resultado_publico.php con pantalla de login con toggle mostrar/ocultar contraseña.
Reply-To SMTP global — todos los emails incluyen la cabecera Reply-To si está configurado.
📊 Status
Sector J — Core File Manifest: verifica integridad de los 90 archivos core con SHA256.
🗄️ BD — migraciones
resultados_anuales: 3 columnas de tracking email (email_tracking_id, email_abierto, email_abierto_at), charset utf8mb4, 3 índices restaurados.
7 claves SMTP duplicadas eliminadas de configuracion (ids 13–19).

v0.5.7 18 de marzo de 2026
📦 Módulo Productos — BD a medida
Nuevas tablas producto_categorias y productos. Lógica PVP calculada en runtime. BD pasa de 12 → 14 tablas.
Plantilla Excel de importación systema_productos_plantilla.xlsx con 4 pestañas. Compatible con Saltoki y otros proveedores.

v0.5.6 18 de marzo de 2026
🔄 Update Engine
Motor de actualizaciones automático con verificación SHA256, backup pre-update y rollback. Parche se aplica sin FTP desde el propio panel.
Generador Core ZIP en actualizaciones.php — genera el ZIP de distribución con manifest SHA256 de los 90 archivos core.

v0.5.5 Anterior 18 de marzo de 2026
🆕 Nuevo módulo: Resultados Anuales
Módulo completo de cierres fiscales — tabla de resultados, modal de creación, portal público por token para el contable.
Generación de ZIPs en background con dompdf — T1, T2, T3, T4 guardados en assets/resultados/{año}/. Estado en tiempo real vía polling.
Email profesional al contable con enlace al portal seguro. Reutiliza motor SMTP.
Tabla comparativa por ejercicio: cobrado, pendiente, vencidas, borradores, total emitido y % cobrado visual.
Nueva tabla BD resultados_anuales con 12 campos incluyendo zip_status, zip_expires_at y token único.
modules/resultados/cleanup.php — cron de limpieza automática de ZIPs caducados (14 días).
🔔 Dashboard
Badge rojo estilo iOS en campana del topbar con número de facturas vencidas.
Modal de vencidas con botones "Fecha hoy" y "Enviar" — flujo correcto: primero actualiza fecha de vencimiento, luego activa el envío.
modules/facturacion/update_vencimiento.php — nuevo endpoint para actualizar fecha sin abrir el modal de edición.
🖥️ Interfaz UI
Zoom configurable 70%–125% en Configuración → Interfaz UI. Guardado en settings.php y localStorage. Sin flash visual al cargar.
modules/configuracion/save_ui.php — endpoint para persistir el zoom en servidor.
🐛 Fixes
Links del modal de alertas — ?buscar=FAC-XXXX ahora navega a facturación y filtra automáticamente.
settings.php ya no se carga dos veces en dashboard — reutiliza $cfg en memoria.
Stats bar: "N resultados" en tungsteno, resto en gris para jerarquía visual clara.

v0.5.4 17 de marzo de 2026
✨ Facturación — Filtros y Excel
Carga inicial filtrada por año actual. Query usa BETWEEN en lugar de YEAR() para aprovechar índice.
Buscador de cliente en modal — dropdown sobre body con position:fixed + getBoundingClientRect().
Exportación Excel con xlsx-js-style: cabecera profesional, datos fiscales, totales desglosados cobrado vs total.
Columnas Cliente e Importe ordenables.
Fix bucle infinito localStorage — año validado contra BD antes de redirigir.
Fix datos fiscales con json_encode en lugar de addslashes.
Fix QR en export PDF múltiple — curl_multi carga QRs en paralelo antes del bucle.

v0.5.3 17 de marzo de 2026
✨ Dashboard — Filtros inteligentes
Subfiltros adaptativos según el selector global. kpi_engine.php reescrito con calcularRango() sin solapamientos.
Fix ReferenceError en actualizarSubfiltros() que tumbaba todo el JS del dashboard.
Fix label donut mostrando valor anterior al cambiar filtro.

v0.4 Anterior Marzo 2026
🐛 Bug fixes
modules/presupuestos/create.php — corregidas referencias a tablas inexistentes (presupuestos_lineaspresupuesto_items) y columnas incorrectas del schema antiguo.
modules/presupuestos/convert.php — corregidas tablas, columnas y FK convertido_factura_id + convertido_at. El flujo presupuesto → factura ahora funciona correctamente.
modules/presupuestos/delete.php — corregida tabla presupuestos_lineaspresupuesto_items.
⚡ Performance
Migración de 9 índices de BD (systema_v04_indexes.sql) para soportar 10.000+ facturas sin degradación.
email_tracking — índice compuesto de 3 columnas con abierto_at para index-only scan en subquery correlated del listado.
presupuestos.fecha_emision — índice añadido (faltaba completamente, causaba full scan en Presentador AEAT).
🛠 Nuevo módulo
status.php — Panel de Status & Mantenimiento con 9 sectores, chip global de estado, acciones con niveles de riesgo y log en tiempo real.
Toggle modo nuclear ↔ white mode en status.php guardado en localStorage.
layout.php — enlace Status añadido al sidebar.
doc/index.html — documentación completa del sistema.

v0.3 14 de marzo de 2026
🆕 Módulo Clientes
Importación masiva desde Excel (.xlsx) con drag & drop y preview de 5 filas.
Exportación a CSV con BOM UTF-8 — compatible con Excel español.
Paginación de 50 registros con ellipsis. Multiselect con bulk eliminar.
🆕 Módulo Facturación
Exportación PDF real con dompdf v2 — individual y bulk (ZIP).
Token público por factura — el cliente ve su factura sin login desde el email.
Paginación de 50 registros. Multiselect con bulk PDF, duplicar y eliminar.
🆕 Módulo Presupuestos
Multiselect con bulk eliminar. Paginación de 50 registros.
⚙️ Infraestructura
dompdf v2.0.4 + Masterminds/html5-php v2.7.6 instalados.
Autoloader PSR-4 manual actualizado.

v0.2 Febrero 2026
Módulo Presentador AEAT — M303, M130, M390 con cálculo automático desde BD.
Historial de presentaciones con snapshot JSON.
Tracking de apertura de emails con pixel 1×1.
Diseño personalizable de facturas con sistema de marcadores.

v0.1 Enero 2026
Versión inicial. Dashboard, Leads, Clientes, Presupuestos, Facturación básica.
Auth por sesión PHP nativa. Config file-based.
Design system con dark/light mode. Arquitectura de módulos REST.
Schema MySQL completo con FKs, vistas y configuración KV.