10. Abonos
ABONOS_ENABLED=false en el .env del servidor apaga todas las rutas /abonos/*. Los controladores (SeasonController, MemberController, DebtController, EventController, CommunicationController, ReportController, ScanController, SpaceController, TagController, DebtBatchController, DoorController, SeasonPassController, TicketController) y las vistas existen en el código fuente, pero no son accesibles desde este panel. Esta sección documenta la funcionalidad a partir del código y debe validarse cuando la sección se reactive.
Introducción
El módulo de Abonos es el corazón de la web de itesalpremios: gestiona las temporadas del club, los miembros / abonados, las deudas (recibos generados y domiciliados), los eventos (partidos, entrenamientos, actos sociales), las comunicaciones enviadas a los miembros y los reports de asistencia e ingresos. Es el módulo específico del segundo sitio del proyecto (itesalpremios) y solo está activo cuando la variable ABONOS_ENABLED=true en el .env del servidor.
Su modelo de datos está pensado para reflejar el ciclo real de un club: una temporada contiene eventos, los miembros pertenecen a una o varias temporadas, cada miembro puede tener deudas (anuales, puntuales, de abonos extra…) que se cobran vía SEPA, y a cada miembro se le pueden enviar comunicaciones (emails, SMS, notificaciones push) sobre los eventos en los que participa.
Acceso rápido
- En este despliegue: no hay entrada en el menú lateral. Todas las rutas
/abonos/*devuelven 404 o respuesta vacía porque el router salta todo el grupo de rutas cuandoABONOS_ENABLED=false. - Con la sección habilitada (referencia):
- Temporadas:
/abonos/seasons - Miembros / abonados:
/abonos/members - Deudas y recibos:
/abonos/debts - Eventos:
/abonos/events - Comunicaciones:
/abonos/communications - Reports:
/abonos/reports - Control de acceso / scanner:
/abonos/scan - Configuración:
/abonos/config - Etiquetas / tags:
/abonos/tags
- Temporadas:
- No existe una ruta raíz
/abonos: el módulo entra directamente por la pantalla de temporadas (/abonos/seasons) que actúa como índice.
Pantalla principal
No hay captura disponible: la sección está deshabilitada en este despliegue (ver COVERAGE.md). La descripción que sigue se basa en el código fuente de los controladores (SeasonController, MemberController, DebtController, EventController, CommunicationController, ReportController) y debe validarse visualmente cuando la sección se reactive.
La pantalla principal del módulo, accesible en /abonos/seasons cuando el módulo está activo, es un listado de temporadas con:
- Tabla con las temporadas (una fila por temporada) y columnas: nombre, rango de fechas, estado (borrador / activa / cerrada), número de miembros, número de eventos, total facturado.
- Acciones por fila: editar, clonar (crea una nueva temporada a partir de otra existente), cerrar temporada, ver reportes.
- Botón Nueva temporada en la parte superior derecha.
- Filtros por estado y por temporada activa.
El panel de cabecera del módulo (visible en /abonos/*) muestra además contadores agregados: miembros totales de la temporada activa, deudas pendientes, eventos próximos en los próximos 7 días, comunicaciones enviadas en los últimos 30 días.
Tareas habituales
10.1 Crear una temporada nueva
Una temporada es el contenedor principal del módulo: agrupa miembros, eventos, deudas y comunicaciones. Suele corresponder al año natural o a la temporada deportiva.
- Entra en
/abonos/seasons. - Pulsa Nueva temporada.
- Rellena:
- Nombre (p. ej. Temporada 2026/27).
- Fecha de inicio y fecha de fin.
- Espacio principal (relación con
Space; ver Referencia técnica). - Tipos de abonado que se ofrecerán (relación con
SeasonPass). Por cada tipo: nombre, precio base, periodicidad (anual / mensual), descripción, edad mínima, edad máxima, número máximo de miembros. - Estado: borrador (no visible) o activa (visible para los flujos de alta).
- Guarda. La temporada se crea en estado borrador; actívala cuando esté lista.
- Si la temporada es similar a una anterior, usa la acción Clonar sobre la temporada de origen para copiar tipos de abonado y configuración.
10.2 Dar de alta un miembro
Los miembros son las personas físicas abonadas al club. Un miembro puede pertenecer a varias temporadas a lo largo del tiempo, y dentro de cada temporada puede tener uno o varios abonos (uno por cada tipo de abonado contratado).
- Entra en
/abonos/members. - Pulsa Nuevo miembro.
- Rellena los datos personales:
- Nombre, apellidos, DNI / NIE / pasaporte.
- Email y teléfono (validación de formato).
- Fecha de nacimiento (necesaria para validar límites de edad de los tipos de abonado).
- Dirección postal (para correspondencia y, en su caso, para SEPA).
- Tags / etiquetas (ver
/abonos/tags) para segmentar.
- Asigna el miembro a una o varias temporadas. Por cada temporada, elige el tipo de abonado (esto genera automáticamente las deudas correspondientes al alta).
- Si el miembro paga por domiciliación bancaria (SEPA), añade los datos del titular y el IBAN. La deuda se generará y se cobrará en la fecha de cargo configurada.
- Guarda. El miembro queda registrado y, si tiene IBAN, entra en el siguiente lote de remesas SEPA.
10.3 Registrar un pago / ver deudas
Las deudas son los recibos que el miembro debe pagar por sus abonos y servicios. Cada deuda tiene un estado: pendiente, cobrada, rechazada, anulada.
- Entra en
/abonos/debts. - Usa los filtros por temporada, miembro, estado y rango de fechas para localizar las deudas.
- Para registrar un pago manual (p. ej. ingreso en cuenta, Bizum, TPV físico), abre la deuda y pulsa Registrar pago. Indica:
- Fecha del pago.
- Importe (puede ser parcial).
- Método de pago (efectivo, transferencia, TPV, Bizum, etc.).
- Referencia / número de operación (opcional, recomendado para conciliaciones).
- Para generar el lote de remesas SEPA del mes:
- Entra en
/abonos/debts/batches(gestionado porDebtBatchController). - Pulsa Nuevo lote, selecciona la fecha de cargo y las deudas pendientes a incluir.
- El sistema genera el fichero SEPA XML y lo deja en estado preparado.
- Envía el fichero al banco (manualmente o vía integración) y marca el lote como enviado.
- Cuando el banco devuelve el resultado, el sistema concilia automáticamente: las deudas enviadas pasan a cobradas o rechazadas según respuesta.
- Entra en
Desde la ficha de una deuda puedes ver su historial completo: alta, envío en lote, intentos de cargo, devoluciones, anulaciones.
10.4 Programar eventos (partidos, entrenamientos)
Los eventos representan cualquier acto del club: partidos, entrenamientos, cenas, presentaciones. Se usan para controlar acceso, registrar asistencia, generar comunicaciones y producir reports.
- Entra en
/abonos/events. - Pulsa Nuevo evento.
- Rellena:
- Nombre (p. ej. Real Madrid — Barça, Entrenamiento infantil).
- Tipo: partido, entrenamiento, evento social, etc.
- Espacio (relación con
Space) donde se celebra. - Fecha y hora de inicio y fin.
- Aforo (opcional; si se deja vacío, no hay límite).
- Precio por entrada (opcional, 0 = acceso con abono).
- Visibilidad: público (aparece en la web), privado (solo gestión interna), solo abonados.
- Permite scanner: sí / no (activa el módulo
ScanControllerpara validar accesos en puerta).
- Guarda. El evento aparece en el calendario de la temporada y, según la configuración, se publica en la web pública.
- Para eventos con venta de entradas, el módulo
TicketControllergestiona la compra online (integración con Stripe, ver sección 7) y la asignación de asientos si procede.
10.5 Enviar comunicaciones a miembros
El módulo de comunicaciones permite enviar emails, SMS o notificaciones push a segmentos de miembros o a asistentes concretos de un evento. Internamente delega en el módulo de mailing para el envío, por lo que reutiliza plantillas y contactos.
- Entra en
/abonos/communications. - Pulsa Nueva comunicación.
- Define el segmento objetivo:
- Todos los miembros de una temporada.
- Miembros con un tag concreto.
- Asistentes confirmados a un evento.
- Asistentes que NO han confirmado.
- Miembros con deudas pendientes o con pagos rechazados.
- Filtro libre por cualquier campo del miembro.
- Elige la plantilla (ver mailing) o redacta el mensaje.
- Selecciona el canal: email, SMS, push, o combinación.
- Programa el envío (inmediato, o a una fecha/hora concreta).
- Revisa la vista previa y los destinatarios estimados. Confirma y envía.
El envío queda registrado con un report accesible desde la ficha de la comunicación: total enviados, abiertos, clics, rebotes, errores.
10.6 Consultar reports (asistencia, ingresos)
Los reports del módulo (ReportController) ofrecen vistas agregadas sobre los datos:
- Asistencia por evento: total asistentes, ocupación del aforo, porcentaje de abonados vs. público general.
- Ingresos por temporada: suma cobrada, suma pendiente, suma rechazada, suma anulada, comparación con temporadas anteriores.
- Evolución de altas / bajas: altas netas por mes, motivos de baja.
- Devoluciones SEPA: listado de recibos rechazados con motivo, importe y miembro asociado.
- Comunicaciones enviadas: rendimiento por campaña, segmentación por canal.
- Cuotas pendientes: miembros con deudas vencidas y no cobradas, ordenados por antigüedad.
Cada report admite exportar a CSV / Excel. Los reports se pueden filtrar por temporada y por rango de fechas.
Referencia técnica (webmasters)
Arquitectura del módulo
El módulo se divide en dos mitades:
- Backend / API (
serv/): controladoresAbonos\*bajoserv/src/controllers/Abonos/(SeasonController, MemberController, DebtController, DebtBatchController, EventController, CommunicationController, ReportController, ScanController, SpaceController, TagController, DoorController, SeasonPassController, TicketController). Exponen la API REST que consume el panel y la web pública. - Panel admin (
herr_admin/): controladores homónimos enherr_admin/src/Controllers/Abonos/que actúan de proxy a la API, renderizando las vistas PHP enherr_admin/src/Views/abonos/. Todas las llamadas pasan porApiClientcon el JWT del admin.
Espacios (Space)
Un espacio es el lugar físico donde se celebran los eventos: el estadio principal, el campo de entrenamiento, el salón de actos. Permite separar temporadas y eventos por recinto, lo que es útil para clubes con varias instalaciones o para limitar aforos.
Domiciliaciones SEPA — SepaService.php
El fichero serv/src/services/SepaService.php se encarga de generar los ficheros SEPA XML (formato SEPA Direct Debit Core) que se envían al banco para cobrar los recibos de los miembros. Su responsabilidad es:
- Generar el XML conforme al esquema del banco (con identificador del acreedor, IBAN del deudor, importes, fechas, referencias).
- Firmar el fichero con el certificado del cliente (cuando se sube vía configuración).
- Generar el justificante de la remesa (PDF) para enviar a los miembros si la ley o la política del club lo requiere.
- Procesar los ficheros de retorno del banco (los que devuelven cobros y rechazos) y conciliar con las deudas internas.
El flujo SEPA es, en resumen: generar lote → firmar XML → enviar al banco → recibir retorno → conciliar. Cada paso deja registro en el DebtBatch correspondiente.
Esquema de base de datos (tablas principales)
Las migraciones del módulo se encuentran en serv/database/migration_abonos*.sql. Las tablas principales son:
abonos_seasons— temporadas.abonos_spaces— espacios / recintos.abonos_season_passes— tipos de abonado dentro de una temporada (precio, periodicidad, edad).abonos_members— miembros / abonados (datos personales, IBAN, tags).abonos_member_seasons— relación N:M entre miembros y temporadas (con el tipo de abonado contratado).abonos_debts— deudas / recibos (importe, fecha de cargo, estado, IBAN al que se cargan).abonos_debt_batches— lotes de remesas SEPA (fecha, fichero, estado).abonos_events— eventos (partidos, entrenamientos, etc.).abonos_event_attendees— asistentes confirmados a un evento.abonos_communications— campañas de comunicación (email/SMS/push) enviadas desde el módulo.abonos_tagsyabonos_member_tags— etiquetas y asignación.
El esquema es relacional, con integridad referencial estricta. Los importes se almacenan en céntimos (enteros) para evitar problemas de redondeo con decimales flotantes.
Relación con otros módulos
- Pagos (sección 7): los tickets de eventos se procesan con el mismo flujo Stripe que el resto del panel. Un evento de pago genera un componente de tipo
payment. - Mailing (sección 8): las comunicaciones del módulo Abonos se enrutan a través del sistema de mailing. Las plantillas, los contactos y los reports de envío son compartidos.
- Formularios (sección 6): el alta de un nuevo miembro desde la web pública se hace con un formulario (
form) que, en su acción de envío, crea elabonos_membery la deuda inicial. Si el alta es con SEPA, se le piden los datos bancarios en el mismo formulario. - Variables globales (sección 11): el módulo expone variables como
ABONOS_NOMBRE_CLUBoABONOS_IBAN_OFICIALque se referencian en los emails y en los PDFs generados.
Rutas internas relevantes
Estas rutas existen en el código fuente, pero no son accesibles en este despliegue (todas responden vacío/404 mientras ABONOS_ENABLED=false):
GET /abonos/seasons— listado de temporadas (pantalla principal del módulo).GET/POST /abonos/seasons/create— alta de temporada.GET/POST /abonos/seasons/{id}— edición de temporada.POST /abonos/seasons/{id}/close— cerrar temporada.POST /abonos/seasons/{id}/clone— clonar temporada desde otra.GET /abonos/members— listado de miembros.GET/POST /abonos/members/create— alta de miembro.GET/POST /abonos/members/{id}— edición de miembro.GET /abonos/debts— listado de deudas.GET /abonos/debts/{id}— ficha de deuda y registro de pago.GET/POST /abonos/debts/batches— gestión de lotes de remesas SEPA.GET /abonos/events— listado de eventos (calendario).GET/POST /abonos/events/create— alta de evento.GET /abonos/communications— listado de comunicaciones.GET/POST /abonos/communications/create— nueva comunicación.GET /abonos/reports— reports agregados.GET /abonos/scan— pantalla de scanner para control de accesos.GET/POST /abonos/config— configuración del módulo (IBAN del acreedor, plantilla SEPA, datos fiscales).GET/POST /abonos/tags— gestión de etiquetas.
Errores frecuentes
- No encuentro la sección Abonos en el menú: comportamiento esperado en este despliegue. La variable
ABONOS_ENABLED=falseoculta todas las entradas del módulo. Para habilitarla hay que: (1) cambiarABONOS_ENABLED=falseaABONOS_ENABLED=trueen el.env, (2) reiniciar el servidor (PHP-FPM, apache, etc.) para que recargue la configuración, y (3) si procede, ejecutar las migraciones de base de datos asociadas al módulo (serv/database/migration_abonos*.sql). - No me deja dar de alta a un miembro porque dice que ya existe: el DNI / NIE / pasaporte y el email son únicos. Localiza al miembro existente en el buscador del listado (por DNI o por email) y comprueba si es un duplicado real o si la persona ya estaba registrada con datos antiguos.
- Una deuda no se actualiza tras registrar un pago: revisa que la deuda estaba en estado pendiente y no anulada (no se puede cobrar una deuda anulada). Si la deuda viene de un lote SEPA, espera a la conciliación automática con el fichero de retorno del banco; en pagos manuales, el estado debería cambiar a cobrada al guardar.
- El evento no se muestra en la web pública: revisa: (1) que el evento está en estado publicado (no borrador ni cancelado), (2) que su fecha es correcta y no en el pasado lejano, (3) que su visibilidad es público o solo abonados (si es privado, solo aparece en el panel), y (4) que la temporada asociada está activa.
- Un recibo SEPA se devuelve con "rechazado": las causas más habituales son: IBAN incorrecto, cuenta del titular cerrada, importe superior al autorizado por el banco, o falta de saldo en la fecha de cargo. Localiza al miembro, corrige el dato que proceda y vuelve a generar la deuda en el siguiente lote.
- Las comunicaciones no se envían: el módulo Abonos delega en el sistema de mailing. Revisa la sección 8 (errores frecuentes de SMTP), comprueba que el segmento no esté vacío (si filtras por un tag o evento sin resultados, el envío queda en estado "sin destinatarios") y verifica que la cola de envío está corriendo.
- No puedo cerrar una temporada: la temporada solo se cierra si no tiene deudas pendientes ni eventos activos. Liquida primero los cobros y, o bien cierra los eventos pendientes o bien cámbialos de temporada.
- El scanner de puerta no reconoce a un abonado: comprueba que el miembro está dado de alta en la temporada activa y que su estado no es baja. Si el carnet o el QR son antiguos, regenera el QR desde la ficha del miembro.