Skip to content

La Máquina de Estados

En un sistema de seguros, la consistencia de los estados es la base de la confianza. Yastubo implementa un patrón de Máquina de Estados Finita (FSM) para asegurar que una póliza o un siniestro nunca entren en una situación de negocio inválida (ej. marcar una póliza como activa sin haber sido pagada).

  • Integridad de Datos: Solo se permiten transiciones lógicas predefinidas.
  • Claridad Operativa: El equipo de soporte puede saber exactamente en qué punto del flujo se encuentra cada cliente.
  • Automatización Segura: Los webhooks de Stripe y otros servicios externos solo pueden disparar cambios de estado permitidos.

Deep Dive Técnico: Transiciones de Póliza

Section titled “Deep Dive Técnico: Transiciones de Póliza”

El módulo de emisión define el ciclo de vida de una póliza mediante el objeto VALID_TRANSITIONS. Este mapa dicta qué estado puede seguir a otro.

OrigenDestinos VálidosRazón de Negocio
DRAFTPENDING_PAYMENT, CANCELLEDEl lead ha sido creado pero no ha iniciado el pago.
PENDING_PAYMENTACTIVE, CANCELLED, IN_ARREARSEl cliente está en el flujo de checkout.
ACTIVEIN_ARREARS, CANCELLED, CASE_REPORTEDPóliza vigente; puede reportar un siniestro.
CASE_REPORTEDCASE_IN_PROGRESS, CANCELLEDSe ha iniciado un proceso de asistencia.

Cuando se intenta cambiar el estado de una póliza, el sistema ejecuta una validación estricta antes de persistir el cambio en la base de datos:

app/modules/emission/state_machine.py
def transition(current: PolicyStatus, target: PolicyStatus) -> PolicyStatus:
# Si el estado destino no está en la lista de permitidos para el estado origen
if target not in VALID_TRANSITIONS.get(current, []):
raise ValueError(f"Transición inválida: {current}{target}")
return target

Si se intenta pasar de DRAFT a ACTIVE directamente, el sistema lanzará un error de validación, forzando a que la lógica pase siempre por el estado de pago.

Ejemplo Práctico: Cambio de Estado tras Pago

Section titled “Ejemplo Práctico: Cambio de Estado tras Pago”
app/modules/emission/service.py
@audited(action="activate_policy", entity="policy")
async def activate_policy(db: AsyncSession, policy_id: uuid.UUID):
policy = await get_policy(db, policy_id)
# Validamos y ejecutamos la transición lógica
new_status = transition(policy.status, PolicyStatus.ACTIVE)
policy.status = new_status
await db.commit()
return policy

Aquí, el servicio de emisión colabora con la FSM para asegurar que la póliza policy_id solo se active si su estado actual lo permite.

State Machine Flow