main content

Cómo implementar un sistema de reservas y citas online con Drupal

Llevo años montando webs en Drupal y la conversación con el cliente que quiere reservas online siempre empieza igual: "tenemos un Excel compartido, dos teléfonos sonando y alguien apuntando huecos a mano". Mientras la agenda cabe en una pestaña del navegador, la cosa aguanta. Cuando pasas de cinco a cincuenta citas al día, aparecen las reservas duplicadas, los huecos muertos entre consultas y el administrativo que dedica media jornada a confirmar por teléfono lo que un email debería haber resuelto solo.

Drupal es buen sitio para resolver esto, sobre todo si ya tienes el portal corporativo encima del CMS y no te apetece meter un iframe de Calendly con su tipografía ajena pegado al lateral. Aquí te cuento qué tira un servidor cuando tiene que montar Drupal Booking en serio: qué módulo contribuido se aprovecha, dónde toca picar código y qué decisiones se pagan caras seis meses después.

Por qué Drupal y no un SaaS cerrado

La regla de negocio rara siempre aparece

En cuanto rascas un poco, cada cliente tiene su matiz. La clínica gestiona disponibilidad por profesional, por especialidad y por tipo de consulta (no es lo mismo una revisión de veinte minutos que una primera visita de una hora). El hotel quiere temporadas, estancia mínima y un margen de overbooking controlado por dirección. El coworking factura por hora, medio día y día completo, con tarifas distintas para socios. El centro de formación necesita lista de espera y validar prerequisitos antes de aceptar la inscripción.

Estas reglas raramente caben en el formulario de configuración de un SaaS. Con tipos de contenido a medida, campos de referencia entre entidades y un par de hooks bien colocados, Drupal te deja modelar la lógica tal cual la dibuja el cliente en una servilleta. No al revés.

Vive dentro de tu web, no flotando en un iframe

Si tu sistema de reservas web es una pieza más del Drupal 10 que ya tienes en producción, comparte sesión, comparte usuarios, comparte caché y comparte diseño. Esto se traduce en menos fricción de UX y, sobre todo, en informes coherentes: las reservas son entidades de Drupal y puedes cruzarlas con vistas, con paneles y con tu CRM sin exportar CSVs los viernes.

Aguanta cuando crece

Drupal mueve portales con tráfico serio. Para un sistema de reservas, el truco está en separar lo que se puede cachear (la página del servicio, la ficha del profesional) de lo que tiene que ir siempre vivo (el calendario con la disponibilidad). Bien afinado, lo segundo es una consulta a un par de tablas indexadas, no un drama.

El stack de módulos que suelo montar

Drupal Commerce + Commerce Booking cuando hay cobro

Si la reserva implica pago, no me invento nada: Drupal Commerce de base y Commerce Booking encima. Commerce gestiona producto, carrito, pasarela y pedido. Booking añade el concepto de slot temporal y lo engancha al producto reservable. Con esto sale de fábrica:

  • Recursos reservables modelados como productos (salas, profesionales, equipos).
  • Slots con duración y precio configurables por tramo horario.
  • Disponibilidad por recurso y fecha consultable desde el front.
  • Cobro completo en el momento o reserva con depósito.

Lo que no sale de fábrica y casi siempre toca tocar: las reglas de cancelación con devolución parcial según antelación. Eso son hooks de pedido y un par de servicios custom.

Webform cuando manda el formulario

Hay proyectos donde la reserva no va por carrito sino por formulario: una primera consulta gratuita, una solicitud de presupuesto, una inscripción a un evento. Para eso uso Webform, que es de lo mejor que tiene el ecosistema. Sin escribir código consigues:

  • Lógica condicional decente (campos que aparecen según respuestas anteriores).
  • Validaciones a medida sin salir del UI.
  • Handlers que disparan emails, llaman a APIs externas o crean nodos de tipo Reserva al enviar.
  • Confirmaciones por correo encadenadas al flujo.

BAT cuando la disponibilidad es el problema gordo

BAT, Booking and Availability Management Tools, no es un módulo de reservas para enchufar y tirar. Es una capa de infraestructura para modelar disponibilidad y precios temporales. Lo elijo cuando el cliente necesita granularidad fina o tarifas dinámicas:

  • Unidades reservables con estados (disponible, reservado, bloqueado, en mantenimiento).
  • Calendarios con resolución de minutos, horas o días.
  • Cálculo de precio según fecha, duración y tipo de recurso.
  • Consultas eficientes sobre rangos largos.

BAT te obliga a entender su modelo de eventos antes de soltar la primera línea de código, pero a cambio te ahorra reinventar la rueda cuando el negocio crece.

FullCalendar para que el usuario vea algo

Para pintar disponibilidad uso el módulo FullCalendar. El cliente final ve un calendario interactivo con los huecos libres y elige sin pensar. El equipo interno, la misma librería en una vista filtrable por recurso, estado y fecha. Es uno de esos componentes que merece la pena dejar bonito desde el día uno, porque el cliente lo va a usar todos los días.

Cómo se estructura el modelo de datos

La arquitectura que repito en la mayoría de proyectos:

  • Tipo de contenido Recurso: lo reservable (profesional, sala, servicio). Lleva nombre, descripción, categoría, foto, duración por defecto y precio base.
  • Tipo de contenido Reserva: cada cita concreta. Referencia al recurso, usuario, inicio, fin, estado del workflow (pendiente, confirmada, cancelada, completada) y notas internas.
  • Entidad de disponibilidad: con BAT o con una tabla custom, define los bloques en que cada recurso acepta reservas.

El flujo del usuario, contado como lo vive desde el front:

  1. Aterriza en la ficha del servicio o del profesional.
  2. Ve el calendario con los huecos de las próximas semanas.
  3. Pica un slot y rellena los datos relevantes para esa cita.
  4. Si hay cobro, pasa por Stripe, Redsys o PayPal según lo que tengas configurado.
  5. Recibe correo de confirmación con enlace para modificar o cancelar.
  6. Veinticuatro horas antes le entra el recordatorio automático por email o SMS.

Y el backoffice que necesita el equipo de gestión:

  • Vista del día, la semana y el mes con filtros por recurso y estado.
  • Crear, editar y cancelar reservas a mano sin pelearse con el formulario público.
  • Bloqueo de días, horarios especiales, vacaciones.
  • Informes mínimos viables: ocupación, cancelaciones, ingresos por servicio, horas punta.

Pasarelas de pago: lo que aplica en España

Redsys

Si el cliente factura en España, casi seguro que su banco le da TPV virtual con Redsys. Commerce Redsys cubre el caso estándar (pago único, tokenización para cobros recurrentes, devoluciones desde el back). La parte que se atasca suele ser el certificado SHA-256 y los entornos de pruebas, no el código.

Stripe

Stripe gana cuando el cliente quiere experiencia moderna o cobra fuera de España. Commerce Stripe trae tarjeta, Apple Pay, Google Pay y SEPA. La tokenización es limpia y los webhooks funcionan bien si dejas la URL correctamente expuesta.

PayPal

Lo dejo como segunda opción para clientes que aún prefieren no soltar la tarjeta. Commerce PayPal cubre tanto el flujo estándar como Express Checkout y se monta en una tarde.

Avisos, recordatorios e integraciones

Email transaccional

Para los correos del ciclo de vida (confirmación, recordatorio a 24 o 48 horas, valoración posterior, aviso de cancelación) configuro Symfony Mailer y lo enchufo a un proveedor transaccional: SendGrid, Mailgun o Amazon SES. Mandar correos críticos desde el SMTP del hosting compartido es buscarse problemas de entregabilidad que luego se pagan en no-shows.

SMS

Para clínicas, restaurantes con reserva y servicios donde el cliente no aparece, el recordatorio por SMS baja el ratio de ausencias de forma medible. Twilio y Vonage tienen integraciones razonables; cuando no encuentro módulo decente, escribo un servicio custom que llama a su API REST desde un hook de cron.

Google Calendar e iCal

Adjuntar un .ics al correo de confirmación cuesta veinte líneas de código y le permite al cliente meter la cita en su Google Calendar de un click. Es de esas mejoras pequeñas que reducen no-shows sin tocar la lógica del sistema de reservas web.

Detalles que se pagan caros si los olvidas

Rendimiento

Cachea las páginas públicas con BigPipe o caché de página estándar. Las consultas de disponibilidad merecen índices propios en las tablas BAT (o en las que te hayas inventado) y, en calendarios largos, lazy loading mes a mes en lugar de cargar el año entero.

Accesibilidad

Los calendarios interactivos son el componente más fácil de romper para usuarios con lector de pantalla. Si no implementas ARIA labels y navegación por teclado decente, no pasas WCAG 2.1 AA. En clientes del sector público esto no es opcional, es contractual.

Concurrencia en el mismo slot

Cuando dos usuarios pican el mismo hueco a la vez, alguien se va a cabrear. Yo uso bloqueo optimista: no reservo el slot al seleccionarlo, valido la disponibilidad justo antes de confirmar y, si llegó tarde, muestro un mensaje claro con los huecos cercanos disponibles. Bloquear pesimistamente el slot al primer click genera más problemas (carritos abandonados que mantienen huecos vivos) de los que resuelve.

Zona horaria

Almacena todo en UTC y muestra en la zona horaria del cliente. Drupal lo gestiona bien de forma nativa, pero la configuración tiene que ser explícita desde el primer día: si dejas el módulo de citas online Drupal corriendo medio año con la zona del servidor por error, migrar las fechas históricas no es divertido.

El primer sprint útil

Lo que rinde en el arranque es no querer cubrir todos los casos a la vez. En el primer sprint conviene levantar Commerce, definir un único tipo de recurso, montar la disponibilidad con BAT, dejar Redsys o Stripe funcionando en sandbox y mandar los dos correos críticos (confirmación y recordatorio). Con eso ya tienes un módulo de reservas en producción que el equipo puede usar a diario. Las reglas raras (overbooking, lista de espera, descuentos por antelación) se añaden en sprints posteriores con datos reales encima de la mesa.

Si quieres revisar tu caso concreto y ver qué módulos contribuidos te cubren y dónde toca código a medida, cuéntanos qué tipo de reservas gestionas y montamos la propuesta sobre tu Drupal 10 actual.