Feature Specification: Caso de uso único de cancelación por el paciente (cancelarCita)
Feature Branch: 005-cancelacion-paciente
Created: 2026-09-30
Status: Draft
Input: User description: "Caso de uso único cancelarCita de CitaClara: cualquier cancelación iniciada por el propio paciente —desde el portal o desde el enlace del recordatorio— ejecuta este caso de uso y ningún otro. Entradas: pacienteId verificado, citaId, origen (portal | email). Reglas: solo actúa sobre citas en estado reservada del propio paciente; exige confirmación expresa previa (visitar el enlace o la página no cancela nada); exige antelación de 24 horas o más (inicio − ahora, borde de 24 h incluido, precisión de minuto, instante «ahora» y zona de tiempo-referencia); es idempotente (la segunda cancelación se rechaza como «ya cancelada»); al aplicarse, la cita pasa a cancelada y su tramo [inicio, fin) queda libre y reutilizable sin violar RN1. Fuera de plazo o sobre estado final, rechaza con el aviso único del catálogo (que muestra el teléfono de Clínica.telefono) y no muta nada. Comportamiento concurrente: doble clic o doble petición simultánea solo aplican una vez."
Clarifications
Session 2026-09-30 (firma, lock, trazabilidad)
- Q1 — ¿Cuál es la firma exacta y el catálogo de avisos? → A:
cancelarCita(pacienteId, citaId, origen)conorigen ∈ {portal, email}, que devuelveaplicadao un rechazo del catálogo único:YA_CANCELADA,ESTADO_FINAL,CITA_AJENA,PLAZO_VENCIDO,ENLACE_INVALIDO. Cada código tiene un único texto es-ES con el teléfono de la clínica (001 FR-001). - Q2 — ¿Dónde vive el lock de concurrencia? → A: en la transacción: leer estado + antelación y escribir
canceladaes una operación atómica. Doble clic, doble petición o cierre simultáneo de recepción: solo una gana; las demás reciben el rechazo correspondiente (YA_CANCELADAoESTADO_FINAL). Esta es la única vía por la que un paciente escribecancelada. - Q3 — ¿Qué trazabilidad queda? → A: cada ejecución registra
origen, instante y resultado, visible para recepción en la ficha de la cita («Cancelada por el paciente desde el portal/email el DD/MM/AAAA a las HH:MM»). El detalle de persistencia se decide en el plan (enmienda menor en la 001 si requiere campo); nunca expone datos de otra ficha. - Q4 — ¿Los criterios sirven para portal y recordatorios? → A: sí. Los escenarios de aceptación de abajo son el contrato reutilizable: portal y recordatorios los referencian en vez de redactar los suyos.
User Scenarios & Testing
User Story 1 — Cancelar en plazo desde cualquier canal (Priority: P1)
Un paciente con una cita futura reservada y 24 h o más de antelación la cancela desde el portal o desde el enlace del email (previa confirmación): la cita pasa a cancelada, el tramo queda libre y ve la confirmación con el aviso único.
Why this priority: Es el ahorro directo para Sonia («la mitad de la mañana» al teléfono) en ambos canales. Sin este camino único, cada canal implementa su propia cancelación y divergen.
Independent Test: Se puede probar por completo tomando una cita reservada en plazo, ejecutando cancelarCita desde cada origen con confirmación y comprobando el paso a cancelada, la liberación del tramo (RN1) y el registro de origen.
Acceptance Scenarios:
- Given una cita
reservadadel paciente coninicio − ahora ≥ 24 h, When confirma desde el portal, Then devuelveaplicada, la cita pasa acancelada, el tramo queda libre y la ficha muestra «Cancelada por el paciente desde el portal el …». - Given la misma situación, When confirma desde el enlace vigente del email, Then devuelve
aplicadacon idéntico efecto y la ficha muestra «… desde el email …». - Given una cita
reservadaen plazo, When se visita la página o el enlace sin confirmar, Then no se ejecuta nada: la cita siguereservaday el tramo ocupado.
User Story 2 — Rechazos que nunca mutan (Priority: P1)
Todo intento fuera de plazo, sobre estado final, sobre cita ajena o duplicado se rechaza con su aviso único y no cambia nada.
Why this priority: La mitad del valor es no romper nada: un rechazo que mutara dejaría huecos fantasma o citas perdidas. Es P1 porque define la seguridad del caso de uso.
Independent Test: Se puede probar por completo con una matriz de rechazos (plazo, final, ajena, duplicada, enlace inválido) comprobando estado inmutable y aviso exacto en cada caso.
Acceptance Scenarios:
- Given una cita
reservadaconinicio − ahorade 23 h 59 min, When se intenta cancelar, Then devuelvePLAZO_VENCIDO(«Ya no se puede cancelar por internet; llama a la clínica al …»), la cita siguereservada. - Given una cita en estado final (
completada,cancelada,no_asistida), When se intenta cancelar, Then devuelveESTADO_FINAL(oYA_CANCELADAsi ya estabacancelada) sin mutar nada. - Given una cita
reservadade otra ficha, When se intenta cancelar con elpacienteIdpropio, Then devuelveCITA_AJENAsin mutar nada ni revelar datos de la otra ficha. - Given una cita ya cancelada por este caso de uso, When se reintenta (doble clic), Then devuelve
YA_CANCELADA(«Esta cita ya está cancelada») sin efectos adicionales.
User Story 3 — Concurrencia que solo aplica una vez (Priority: P2)
Doble clic, doble petición simultánea o cierre de recepción en el mismo instante: exactamente una ejecución aplica; las demás reciben el rechazo que corresponda.
Why this priority: Sin atomicidad, dos escrituras simultáneas pueden duplicar efectos o violar RN1. P2 porque presupone US1/US2 y es garantía técnica, no flujo de usuario.
Independent Test: Se puede probar por completo lanzando N ejecuciones concurrentes sobre la misma cita y comprobando que exactamente una devuelve aplicada.
Acceptance Scenarios:
- Given una cita cancelable, When llegan dos confirmaciones a la vez, Then una devuelve
aplicaday la otraYA_CANCELADA; la cita quedacanceladauna sola vez. - Given una cita
reservadaen plazo, When recepción la marcacompletadamientras el paciente confirma, Then solo una de las dos transiciones aplica; la perdedora recibeESTADO_FINALoYA_CANCELADAsin estados intermedios.
Edge Cases
- ¿Qué pasa si la cita está «En curso» (inicio pasado, fin futuro)?
inicio − ahora < 24 h, luegoPLAZO_VENCIDO: coherente con el portal (no cancelable) sin regla adicional. - ¿Qué pasa si
inicio − ahoraes exactamente 24 h? Dentro de plazo (borde incluido, 006 FR-005). - ¿Qué pasa si el
origenno es portal ni email? Se rechaza como invocación inválida sin mutar nada; solo esos dos orígenes existen en v1. - ¿Qué pasa si la cita se mueve entre la visualización y la confirmación? La validación usa los datos vigentes en la transacción (006 FR-001): si ya no hay plazo,
PLAZO_VENCIDO. - ¿Qué pasa si el paciente cancela una cita que ya tiene recordatorio enviado? El envío queda como histórico (recordatorios); el caso de uso no toca envíos.
- ¿Qué pasa si la identidad no está verificada (enlace sin token válido)?
ENLACE_INVALIDOantes de cualquier otra validación; no se revela si la cita existe.
Requirements
Functional Requirements
- FR-001 (firma): El sistema MUST exponer
cancelarCita(pacienteId, citaId, origen)conorigen ∈ {portal, email}como única vía por la que un paciente escribecancelada. Portal y recordatorios MUST llamarlo y MUST NOT implementar su propia transición. Devuelveaplicadao un código del catálogo FR-005. - FR-002 (identidad): El
pacienteIdMUST estar verificado según007-identidad-pacienteantes de invocar. Si la cita no pertenece a esa ficha MUST devolverCITA_AJENAsin mutar nada ni revelar datos ajenos. - FR-003 (confirmación): La ejecución MUST exigir confirmación expresa previa del paciente en su canal. Visualizar la página o el enlace MUST NOT ejecutar nada.
- FR-004 (plazo y estado): Solo actúa sobre citas en estado
reservadaconinicio − ahora ≥ 24 h, computados según006-tiempo-referencia(FR-001…FR-005 de esa spec). Esta spec MUST NOT redefinir «ahora», bordes ni precisión. Fuera de plazo →PLAZO_VENCIDO; estado final →ESTADO_FINAL(YA_CANCELADAsi ya estabacancelada). - FR-005 (catálogo único): Los códigos y textos son únicos para ambos canales, en español de España y con el teléfono de la clínica (001 FR-001):
YA_CANCELADA(«Esta cita ya está cancelada»),ESTADO_FINAL(«Esta cita ya no se puede cancelar; llama a la clínica al …»),CITA_AJENA(genérico, sin revelar datos),PLAZO_VENCIDO(«Ya no se puede cancelar por internet; llama a la clínica al …»),ENLACE_INVALIDO(«Este enlace ya no es válido; entra al portal o llama a la clínica al …»). Ningún canal MUST redactar los suyos. - FR-006 (atomicidad): Leer estado + antelación y escribir
canceladaMUST ser una transacción atómica. Concurrentemente, como máximo una ejecución devuelveaplicadapor cita; las demás devuelvenYA_CANCELADAoESTADO_FINAL. MUST NOT quedar nunca dos efectos ni estados intermedios. - FR-007 (efecto): Al aplicarse, la cita MUST pasar a
canceladay su tramo[inicio, fin)MUST quedar libre y reutilizable sin violar RN1 (001 FR-007/FR-011). MUST NOT tocar envíos, métricas ni otras citas. - FR-008 (trazabilidad): Cada ejecución MUST registrar
origen, instante y resultado, visible en la ficha como «Cancelada por el paciente desde el portal/email el DD/MM/AAAA a las HH:MM». El detalle de persistencia se decide en el plan (enmienda menor 001 si requiere campo). MUST NOT exponer datos de otra ficha. - FR-009 (alcance): Esta feature MUST NOT definir identidad (ver 007), tiempo (ver 006), contenido del email ni de la página del portal, y MUST NOT tocar
completadanino_asistida(solo recepción, 001 FR-011/FR-012). EscribeCita.estado(reservada → cancelada); propietario de la tabla: 001.
Key Entities
- Ejecución de
cancelarCita: Invocación (pacienteIdverificado,citaId,origen) con resultado (aplicadao código de rechazo). Atómica; idempotente; traza origen/instante/resultado. - Catálogo de rechazos: Cinco códigos con texto único es-ES (FR-005). Compartido por portal y email.
- Cita (escritura delegada): Transición
reservada → canceladapropiedad de la 001; este caso de uso es su único invocador por parte del paciente.
Success Criteria
Measurable Outcomes
- SC-001: Una cita en plazo se cancela desde cada origen con confirmación y queda
canceladacon tramo libre (RN1 en verde) y traza de origen, en el 100 % de los intentos. - SC-002: La matriz de rechazos (plazo, final, ajena, duplicada, enlace inválido) devuelve el código y texto exactos sin mutar nada, en el 100 % de los casos.
- SC-003: N ejecuciones concurrentes sobre la misma cita producen exactamente una
aplicada; cero dobles efectos y cero estados intermedios. - SC-004: Cero transiciones
reservada → canceladapor paciente fuera de este caso de uso (revisión + grep por merge en portal y recordatorios). - SC-005: Todos los textos del catálogo están en español de España con el teléfono de la clínica y el formato de tramo «DD/MM/AAAA, HH:MM–HH:MM».
Assumptions
- Identidad verificada según 007 (incluido el enlace verificado FR-006 de esa spec); el enlace sin verificar sigue siendo deuda temporal hasta adoptarlo.
- Tiempo («ahora», bordes, precisión, zona) según 006; aquí no se redefine.
Clínica.telefonoobligatorio (001 FR-001) para todos los avisos.- Recepción conserva sus transiciones (
completada,no_asistida, cancelación propia) por 001 FR-011/FR-012; la carrera con el paciente la gana quien confirme primero en la transacción. - Confirmación expresa, textos de email/portal y métricas viven en sus specs; aquí solo el contrato del caso de uso.