Estados de asientos y tipos de retención

Seatmap.pro rastrea cada asiento de cada evento mediante un modelo de estados de dos capas. La primera capa es un conjunto cerrado y reducido de estados de ciclo de vida que el motor de reservas usa para la concurrencia y la disponibilidad. La segunda capa es un holdType abierto, definido por el cliente, que aporta el contexto de negocio: por qué se retiene este asiento, quién puede liberarlo y cómo debe verse en el mapa de asientos.

Las dos capas

Capa Columna Valores Controla
Ciclo de vida state enum cerrado concurrencia, disponibilidad
Tipo de retención holdType cadena abierta política de liberación, pago, visualización

El motor de reservas solo consulta state = 'ACTIVE' para determinar si un asiento puede bloquearse. El holdType son metadatos que rigen el flujo de negocio en torno a ese asiento.

Estados de ciclo de vida

Cuatro estados, fijados por la plataforma.

Estado Significado Reservable
ACTIVE Disponible para selección
LOCKED Retenido, pendiente de acción No
SOLD Confirmado y completado No
BLOCKED No disponible operativamente No

Un flujo de carrito típico mueve un asiento de ACTIVELOCKEDSOLD. Un carrito abandonado lo devuelve de LOCKEDACTIVE. Un asiento defectuoso permanece en BLOCKED hasta que operaciones lo libera.

Tipos de retención

El holdType es una cadena que asocias a un asiento cuando sale de ACTIVE. Convive junto al estado de ciclo de vida y describe el motivo por el que se retiene el asiento.

Valores predeterminados de la plataforma

Se incluyen dos tipos de retención de fábrica:

Tipo de retención Estado válido Liberado por Pago
CART LOCKED cliente, admin, sistema requerido
RESERVED LOCKED admin no requerido

CART es el valor predeterminado del endpoint /lock de reservas v2: omitir holdType en el cuerpo de la solicitud se resuelve como CART.

Nota sobre la expiración automática: el campo ttlSeconds de abajo es aceptado por la API de configuración y se almacena en HoldTypeDefinition, pero la liberación automática del bloqueo basada en TTL aún no la aplica el servicio de reservas. Las retenciones persisten hasta una llamada explícita a /unlock, /sale o /revertsale. La aplicación del TTL (escribir seat_on_event.locked_until al bloquear y un proceso de limpieza en segundo plano) está en la hoja de ruta.

Definir tus propios tipos de retención

Cada organización puede declarar tipos de retención adicionales mediante la columna JSONB config en organization (y opcionalmente heredar del tenant padre). La configuración se lee a través de la API de configuración estándar de organización/inquilino usada para otras funciones como los webhooks.

Ejemplo de payload enviado a POST /api/organizations/config/:

{
  "holdTypes": {
    "PARTNER_HOLD": {
      "validStates": ["LOCKED"],
      "ttlSeconds": 3600,
      "releaseApi": "ADMIN,API",
      "paymentRequired": true,
      "displayName": "Partner Hold",
      "displayColor": "#8B008B"
    },
    "COMP": {
      "validStates": ["LOCKED", "SOLD"],
      "ttlSeconds": null,
      "releaseApi": "ADMIN",
      "paymentRequired": false,
      "displayName": "Complimentary",
      "displayColor": "#FFD700"
    }
  }
}

Referencia de campos:

Campo Tipo Descripción
validStates arreglo de cadenas Para qué estados de ciclo de vida es válido este tipo de retención. El servicio de reservas rechaza las solicitudes que combinan un emparejamiento no válido.
ttlSeconds entero o null Ventana de autoliberación prevista en segundos. Se almacena como metadato de configuración; la autoliberación aún no se aplica (ver nota arriba). null significa que la retención persiste hasta liberarse explícitamente.
releaseApi cadena Lista separada por comas de actores autorizados a liberar: CUSTOMER, ADMIN, SYSTEM, API.
paymentRequired boolean Si esta retención debe ir seguida de un pago para convertirse en SOLD.
skipLockedState boolean Cuando es true, transita directamente ACTIVESOLD (usado para flujos de taquilla).
displayName cadena Etiqueta legible mostrada en las interfaces de administración.
displayColor cadena Color hexadecimal para el estilo del renderizador.
displayIcon cadena Referencia opcional a una imagen o icono para el renderizador.

Herencia a nivel de inquilino

Si tu organización pertenece a un inquilino, puedes definir tipos de retención a nivel de inquilino una sola vez y se aplicarán a todas las organizaciones del inquilino. Las anulaciones por organización tienen prioridad campo por campo, de modo que una organización puede, por ejemplo, anular el displayColor de CART sin reescribir la definición completa.

Orden de resolución para cualquier búsqueda de tipo de retención:

  1. config.holdTypes[name] a nivel de organización
  2. config.holdTypes[name] a nivel de inquilino
  3. Valores predeterminados de la plataforma

El resolutor combina estas tres capas. Si solo anulas displayColor a nivel de organización, el resto de la definición se hereda del inquilino o del valor predeterminado de la plataforma.

Uso de tipos de retención en la API de reservas

El payload StateSelection de reservas v2 acepta un campo opcional holdType:

POST /api/private/v2.0/booking/lock?eventId=123e4567-e89b-12d3-a456-426614174000
Content-Type: application/json

{
  "sessionId": "abc123",
  "seats": [{ "id": 42 }],
  "holdType": "PARTNER_HOLD"
}

El servicio de reservas valida:

  • Que el tipo de retención esté definido (como valor predeterminado de la plataforma, entrada a nivel de inquilino o entrada a nivel de organización) para la organización que realiza la llamada.
  • Que validStates incluya el estado de ciclo de vida de destino.

Los tipos de retención desconocidos o no coincidentes devuelven 400 Bad Request. Los endpoints /unlock y /revertsale no aceptan holdType: devolver un asiento a ACTIVE borra cualquier retención asociada.

Concurrencia y el bloqueo condicional

Cada transición de estado es una actualización SQL condicional que afirma el estado actual esperado en su cláusula WHERE. Dos solicitudes concurrentes que bloquean el mismo asiento no pueden tener éxito ambas: la actualización de la segunda solicitud afecta a cero filas y la API devuelve false. Esto hace que la doble reserva sea imposible en la capa de base de datos, independientemente de la temporización del flujo de carrito.

Registro de auditoría

Cada transición de estado exitosa añade una fila inmutable a seat_state_events, registrando:

  • Estado de ciclo de vida antiguo y nuevo
  • Tipo de retención en el momento de la transición
  • ID de sesión (cuando está disponible)
  • Asiento o grupo de asientos afectado
  • Marca de tiempo

Este registro de solo adición es la fuente de verdad para la analítica, sustituyendo la telemetría de mejor esfuerzo que la precedía. Los informes posteriores pueden responder preguntas como «qué porcentaje de retenciones CART se convirtió en SOLD la semana pasada» sin tocar la ruta crítica de reservas.

Integración con el renderizador

El Booking Renderer aplica estilo a los asientos según su estado de ciclo de vida (ACTIVE, LOCKED, SOLD) de fábrica. Para visualizar valores holdType personalizados – o cualquier otra distinción – con tus propios colores, animaciones o iconos, el SDK del renderizador proporciona setSeatsState(seats, stateKey) y clearSeatsState(seats), controlados por una configuración declarativa theme.seatStyles. Lee los payloads de los asientos (incluido su holdType) y aplica un estado de renderizador coincidente a los ID de asiento correspondientes. Consulta Estados de asiento personalizados (Renderer SDK) para conocer la API completa, la referencia de tipos y ejemplos resueltos.

Relacionado

  • Estados y estilo de secciones – el concepto equivalente aplicado a los contornos de sección (resaltado, seleccionado, no disponible, filtrado).