Llaves e intercambio
Las llaves son la moneda del intercambio entre copropietarios. Todo lo relativo a llaves vive en v2 (Postgres); el intercambio en sí lo dispara v1 (Firestore) y consulta el wallet de v2.El wallet de llaves
El saldo se guarda en la tablaexchange_keys_wallet, con registros que tienen fecha de caducidad. Los movimientos se registran en exchange_keys.
- Caducidad: 24 meses. Cada asignación de llaves caduca a 24 meses de su fecha de creación (
src/bookings/v2/bookings.repository.ts:387; tambiénsrc/bookings/v1/bookings.repository.ts:1712). - Consumo FIFO por caducidad. Al gastar llaves se descuentan primero las que caducan antes (
expiresAtascendente) (bookings.repository.ts:407). - Las asignaciones con la misma fecha de caducidad se acumulan en el mismo registro.
Movimientos: los 12 conceptos
Cada movimiento tiene unconcept (src/common/database/v2/entities/bookings/exchange-key.entity.ts:10):
Bonos
- Bono de fidelización (
FIDELIZATION_BONUS): +2 llaves si el usuario tuvo 2 o más intercambios válidos en el año (de tipo intercambio, reservados, no propios). Lo otorga el cron de fin de año (src/bookings/v2/commands/yearly-bonus.command.ts:63). - Bono de referido (
REFERRAL_BONUS): +2 llaves, mediante el endpoint adminPOST /v2/admin/bookings/keys/:userId/add-referral-bonus(src/bookings/v2/bookings-admin.controller.ts:40). - Regalo de bienvenida (
WELCOME_GIFT): al recibir llaves por su primer intercambio (flagfirst_exchange), el código otorga un bono igual al importe recibido — es decir, duplica la ganancia de ese primer intercambio (src/bookings/v1/bookings.repository.ts:1763).
Caducidad: el cron y la prórroga
El cron de caducidad corre a diario y comprueba todos los monederos (src/bookings/v2/commands/key-expires.command.ts):
- Avisos previos. Si a un registro le quedan tantos días como alguno de los configurados en
keys_expiration_warning_days(CSV en la tablaconfigurations), se genera una notificación (key-expires.command.ts:43). - Primera caducidad → prórroga automática de 12 meses. Si un registro caduca hoy y el usuario no ha sido prorrogado antes (
user.prorrogatedKeys), se le amplía la caducidad 12 meses, una sola vez (key-expires.command.ts:88). - Segunda caducidad → se pierden. Si el usuario ya fue prorrogado, las llaves caducadas se eliminan y se registra un movimiento
EXPIRATION(key-expires.command.ts:113).
Los dos motores de intercambio
Conviven dos sistemas de intercambio. Cuál se usa lo decide la flagNEW_EXCHANGE_SYSTEM (más una lista de usuarios habilitados, NEW_EXCHANGE_SYSTEM_ENABLED_USERS) (src/bookings/v1/bookings.repository.ts:196).
- Motor viejo — match por fechas
- Motor nuevo — marketplace de llaves
Al publicar una reserva para intercambio (
to_swap), el sistema busca automáticamente otras reservas de intercambio con fechas de entrada y salida exactamente iguales en otras casas, y notifica a sus dueños cuando hay coincidencia (src/bookings/v1/bookings.service.ts:486 y :803). Es un emparejamiento de reservas, sin cobro instantáneo de llaves.Excepción de lanzamiento (regla temporal, YA CADUCADA)
Hubo una regla de lanzamiento con fecha fija en el código: hasta el 15/03/2026, en temporada media o alta y con ≥ 6 meses de antelación, el cobro también era instantáneo (src/bookings/calendars/v1/calendars.service.ts:288, duplicada en src/bookings/v1/bookings.repository.ts:221). Esa fecha ya pasó: la condición sigue en el código pero está inerte (now < 15/03/2026 ya nunca se cumple). Candidata a limpieza en vivla-api; si reaparece un comportamiento de cobro instantáneo con 6-8 meses, no es un bug de esta doc — sería que alguien reactivó la fecha.
Coste en llaves de una reserva de intercambio (v2)
Al registrar un intercambio desde la app o el panel, el coste en llaves se calcula siempre en el momento de validar los slots, haya o no una estancia asociada a la reserva (src/bookings/v2/admin/bookings.admin.service.ts:630; src/bookings/v2/apps/bookings.apps.service.ts:1134). El coste es:
- Última hora → 1 llave fija (
bookings.apps.service.ts:1279;bookings.admin.service.ts:630). - Resto de intercambios → coste de la temporada de la casa × número de slots. El coste por temporada sale de la tabla de equivalencias de la casa (
calculateSlotKeys,src/bookings/v2/bookings.common.repository.ts:629).
calculateSlotKeys devuelve 0 y el registro del intercambio se rechaza con el error KEYS_NOT_CONFIGURED (“No hay un coste en llaves configurado para la temporada de la casa seleccionada”) (bookings.apps.service.ts:1140; bookings.admin.service.ts:637). Es una salvaguarda para no crear intercambios de 0 llaves.
Al cambiar el tipo de una reserva a intercambio desde el panel, la reserva debe tener una temporada asociada en su slot; si no la tiene no se puede convertir (se necesita para calcular las llaves) (bookings.common.repository.ts:2270).
ThirdHome
ThirdHome es la red externa de intercambio de casas. Envivla-api no hay integración por API: no se hace ninguna llamada saliente a ThirdHome. Todo el flujo son transiciones de estado manuales que gestiona un admin desde el panel (src/bookings/v1/bookings.service.ts:1952; endpoints en bookings-admin.controller.ts:183).
La máquina de estados sobre una reserva de disfrute (OWN) activa y aprobada:
Como no hay integración, el cruce real con ThirdHome ocurre fuera del sistema; la API solo refleja el estado que el equipo va marcando a mano.