Hospederías es una aplicación web que escribí para dejar de hacer a mano los partes de viajeros de mis alojamientos de alquiler vacacional. Recoge los datos de los huéspedes antes de que lleguen y los comunica al Ministerio del Interior por su servicio web, como pide el Real Decreto 933/2021. No es un producto comercial ni un servicio abierto al público: la uso para gestionar mis propios apartamentos.
El nombre es solo el del repositorio y el del proyecto. No tiene relación con SES.HOSPEDAJES, la plataforma oficial del Ministerio, ni con las redes hoteleras que usan la palabra «Hospederías» en su nombre. Es una aplicación independiente que habla con el servicio web público de la Administración.
Por qué existe
Antes de la aplicación, el proceso era una cadena de herramientas: un formulario de Google Forms recogía los datos, las respuestas acababan en una hoja de Google Sheets con una fila por parte, copiaba la fila a una segunda pestaña y la exportaba a CSV, un script de Python en línea de comandos convertía ese CSV en el XML del Ministerio y subía el XML a mano en su web.
La primera versión de la aplicación (mayo de 2025) solo hacía el último tramo: recibía un CSV y devolvía el XML. Lo demás vino después: formulario propio, varios usuarios y alojamientos, envío directo al servicio web, consulta del estado y calendario.
De aquel origen queda una huella visible: cada parte se sigue guardando como un CSV con las mismas columnas que exportaba Google Sheets, incluidas cosas como Nombre.1 o Registrar otro viajero ?.
Qué hace hoy
El formulario del huésped
Cada alojamiento tiene un enlace propio con un token aleatorio que lo identifica sin exponer su código oficial. El huésped principal abre ese enlace y rellena sus datos sin cuenta ni contraseña: nombre, apellidos, nacionalidad, documento de identidad, teléfono, fecha de nacimiento, dirección, provincia, código postal y fechas de entrada y salida. Si viaja con más gente, los acompañantes aparecen uno a uno al marcar «Registrar otro viajero», hasta cuatro viajeros por parte.
El selector de fechas es un único campo de rango, en español. La lista de países está en español, con España preseleccionada; la provincia es un desplegable si el país es España y texto libre si no lo es. El código postal se valida mientras se escribe contra la tabla de municipios. El botón de envío no se activa hasta que todo lo obligatorio es válido.
El panel
Cada envío se guarda como un parte y aparece en el listado con alojamiento, huésped, número de viajeros, fechas de estancia, fecha de registro y estado. Hay filtros por estado, ordenación, búsqueda y paginación, y una vista adaptada a móvil donde las fechas se abrevian («Martes 22 → Jueves 24») y los botones se quedan solo con el icono. Desde la ficha de cada parte se puede revisar, editar —solo si aún no se ha comunicado o si dio error—, eliminar con borrado blando, o devolver un parte rechazado a «nuevo» para reenviarlo. Si el aviso por correo está activado, cada parte nuevo manda un resumen en HTML al propietario.
La comunicación con el Ministerio
Desde el panel se puede generar y descargar el XML oficial altaParteHospedaje para subirlo a mano, o enviarlo directamente al servicio web con un clic, previa confirmación. El Ministerio responde con un código de lote; con «Comprobar parte» se consulta el lote y el parte pasa a procesando, validado o rechazado, guardando el código de comunicación y la respuesta. Con ese código se puede ver qué tiene registrado el Ministerio en una pantalla legible, junto a la respuesta completa. Los datos del alojamiento —habitaciones, conexión a internet— se añaden solos al XML desde su ficha.
El calendario de ocupación
/calendar-view muestra doce meses en filas y los días en columnas, con los fines de semana sombreados. Cada estancia es una píldora que se puede pulsar para abrir el parte. Cada alojamiento tiene un color base y cada huésped recibe un tono de ese color; las estancias que se solapan van en carriles distintos y las que cruzan de mes continúan en la fila siguiente.
Alojamientos, usuarios y cuenta
Varios usuarios, cada uno con sus propios alojamientos y partes. Cada alojamiento se da de alta con nombre, código de establecimiento en el Ministerio (único), habitaciones, tipo, internet, contacto, dirección y color de calendario. En el perfil se guardan los datos personales, la contraseña y las credenciales del servicio web del Ministerio.
En desarrollo o sin implementar. El despliegue en el servidor es manual —clonar y copiar ficheros— y automatizarlo está previsto. El alta de usuarios se hace por línea de comandos, no desde la interfaz. No hay soporte para más de cuatro viajeros por parte. El tipo de documento, el prefijo telefónico y la forma de pago se envían con valores genéricos. La anulación de comunicaciones ya enviadas no está implementada, aunque el servicio del Ministerio la permite.
Cómo está hecho
Python 3 con Flask 3.1, organizado en blueprints (público, autenticación, administración, servicio web y perfil). Flask-Login para las sesiones, Flask-SQLAlchemy como ORM, Flask-WTF para la protección CSRF de los formularios y Flask-Mail para los avisos. SQLite guarda usuarios, alojamientos, el estado de los partes y la tabla de códigos postales. La interfaz son plantillas Jinja con Bootstrap 5, DataTables 2 para la tabla de partes, Flatpickr para el rango de fechas, Font Awesome para los iconos y CSS propio para el calendario. Se despliega en un servidor propio con Apache y mod_wsgi. La aplicación no usa ningún modelo ni proveedor de IA. No hay pruebas automatizadas: hay ficheros XML de ejemplo y una lista de comprobaciones manuales antes de cada paso a producción.
CSV como formato de almacenamiento
Cada parte es un fichero CSV de una sola fila en disco, en una carpeta por usuario y alojamiento, con el mismo formato que exportaba Google Sheets. Así pude reutilizar el generador de XML original sin reescribirlo cuando pasé del formulario de Google a uno propio. La base de datos guarda solo el estado y las respuestas del Ministerio.
SOAP escrito a mano
No uso una librería SOAP. El sobre se construye como texto y se envía como multipart/related con autenticación HTTP básica, imitando la petición que generaba SoapUI, la herramienta con la que hice las primeras pruebas contra el servicio. El XML va dentro de un ZIP, codificado en Base64, dentro del sobre. Hay entorno de pruebas y de producción, y el proyecto de SoapUI está en la documentación del repositorio.
El código postal no basta
El Ministerio pide el código de municipio del INE y el huésped solo conoce su código postal. La aplicación carga una tabla de unas 14.600 correspondencias entre código postal y municipio, sacada de un conjunto de datos público, y valida el código postal en directo cuando la nacionalidad es España.
Los países y las tildes
El formulario ofrece los países en español y la aplicación los convierte al código ISO 3166-1 alfa-3 que exige el Ministerio, con una tabla propia de equivalencias. Un commit de enero de 2026 tuvo que quitar la tilde de «MÉXICO» para que la conversión funcionara.
Borrado blando y edición segura
Eliminar un parte mueve su CSV a una carpeta deleted/ y marca el registro como eliminado; no se borra nada. Solo se pueden editar partes que aún no se han comunicado o que han dado error, y el CSV se reescribe de forma atómica —fichero temporal y sustitución— para no dejarlo a medias. En lugar de una herramienta de migraciones, la aplicación añade al arrancar las columnas o índices que falten en SQLite.
Lo que tiene de interesante
Un error que perdía viajeros: al editar un parte, las columnas que el formulario no mostraba —como «Registrar otro viajero ?»— se vaciaban, y al generar el XML desaparecían los acompañantes. La corrección conserva esas columnas y escribe el fichero de forma atómica.
Partes huérfanos: los partes eliminados en versiones antiguas dejaban registros sin estado coherente en la base de datos; se arregló con un estado eliminado, un script de auditoría de solo lectura y otro de corrección con modo de prueba.
Refactorizar un app.py de más de mil líneas: en abril de 2026 dividí la aplicación en blueprints y servicios, manteniendo alias para que las rutas antiguas siguieran funcionando.
Dibujar un calendario sin librerías de calendario: CSS Grid con variables por estancia (--start, --end, --lane) y un algoritmo voraz para colocar las estancias solapadas en carriles.
Estado
Es un proyecto personal en uso real. El historial de git registra un paso a producción con varios usuarios y alojamientos el 20 de mayo de 2025, y el envío de partes al entorno de producción del Ministerio funcionando el 1 de junio de 2025. Sigue en desarrollo activo, aunque con pausas: primer commit el 8 de mayo de 2025, refactorización completa y calendario de ocupación en abril de 2026, mejoras de seguridad básicas en septiembre de 2026 y ajustes para móviles el 23 de septiembre de 2026. Son 85 commits en total. Las etiquetas de versión internas son 0.9.x (v0.9.4 a v0.9.7 entre mayo y junio de 2025), aunque el pie de página muestra el hash del commit desplegado en lugar de un número de versión.
