← Volver al índice de anexos
Macrobloque 4·Inteligencia·Anexo 20 / 40

Anexo 20 · Reglas de negocio (DSL)

Etapa: Fase 6.1
NDA OBLIGATORIO ACTIVO PROPIO

ANEXO 20

Reglas de negocio FARO

Este anexo corresponde a la Fase 6 — Inteligencia, etapa “Reglas de negocio”. Es la capa donde FARO Connect convierte KPIs, señales, objetivos, umbrales y relaciones entre datos en condiciones lógicas que permiten activar alertas, tensiones, diagnósticos, recomendaciones, acciones, responsables, escalamiento y FARO Score.


1. Objetivo del anexo

El objetivo del Anexo 20 — Reglas de negocio FARO es definir bajo qué condiciones FARO debe interpretar que algo requiere atención.

La pregunta central es:

¿Qué condición debe cumplirse para que FARO diga: “esto importa”?

Ejemplo simple:

Si margen bruto < 20%
y descuento promedio > 10%
entonces activar alerta de margen crítico
y evaluar tensión de crecimiento no rentable.

Sin reglas de negocio, FARO tendría datos, KPIs y señales, pero no sabría cuándo actuar.


2. Tesis del Anexo 20

La tesis es:

Las reglas de negocio son el puente entre medir y dirigir.

Hasta ahora FARO tiene:

Datos
→ KPIs
→ Objetivos
→ Umbrales
→ Señales

Pero falta definir:

Si pasa esto
+ en este contexto
+ con esta severidad
+ y esta confianza
→ entonces hacer esto.

Eso es una regla de negocio.

Ejemplo:

Dato:
Descuento promedio 12%.

KPI:
Descuento promedio.

Señal:
Descuento alto.

Regla:
Si descuento > 10% y margen < 22%, generar alerta.

Alerta:
Margen deteriorado por descuento.

Tensión:
Crecimiento no rentable.

Acción:
Auditar descuentos mayores al 8%.

Responsable:
Gerente Comercial.

3. Qué es una regla de negocio FARO

Una regla de negocio FARO es una condición lógica que evalúa datos y define una consecuencia.

Estructura básica:

SI condición
ENTONCES consecuencia

Pero en FARO debe ser más completa:

SI condición de negocio
Y calidad de dato suficiente
Y contexto aplicable
ENTONCES activar señal / alerta / tensión / acción / escalamiento / score

Ejemplo:

{
  "rule_code": "RULE_MARGIN_DISCOUNT_001",
  "name": "Margen bajo con descuento alto",
  "condition": {
    "gross_margin_rate": "< 0.22",
    "discount_rate": "> 0.10"
  },
  "then": {
    "alert": "margen_critico_con_descuento_alto",
    "tension": "crecimiento_no_rentable",
    "responsible": "Gerente Comercial",
    "suggested_action": "auditar_descuentos_altos"
  }
}

4. Diferencia entre señal, regla, alerta y tensión

Concepto Qué hace Ejemplo
Señal Detecta movimiento relevante. Margen cayó 7 puntos.
Regla Evalúa si ese movimiento cumple una condición. Si margen < 22% y descuento > 10%.
Alerta Avisa formalmente un riesgo. Margen crítico por descuento alto.
Tensión Interpreta contradicción entre variables. Ventas suben, margen baja y cobranza empeora.
Acción Define qué hacer. Auditar descuentos mayores al 8%.

La regla es el “cerebro lógico” que decide cuándo una señal pasa a ser algo accionable.


5. Tipos de reglas FARO

FARO debería manejar varias familias de reglas.

Tipo de regla Qué evalúa Ejemplo
Reglas de validación Si el dato sirve o no. Venta sin producto se rechaza.
Reglas de calidad Si el dato es confiable. Costo faltante en 20% de ventas.
Reglas de KPI Si un indicador está fuera de rango. Margen < 20%.
Reglas de señal Si una variación es relevante. Margen cae más de 5 puntos.
Reglas de alerta Si debe emitirse un aviso. Stock bajo mínimo.
Reglas de tensión Si varias señales forman contradicción. Ventas suben + margen baja + cobranza empeora.
Reglas de acción Qué acción sugerir. Auditar descuentos altos.
Reglas de responsable A quién asignar. Stock crítico → Responsable de Stock.
Reglas de escalamiento Cuándo subir de nivel. Acción crítica vencida 48 hs.
Reglas de score Cómo impacta FARO Score. Tensión crítica abierta resta puntos.
Reglas por industria Adaptación sectorial. RevPAR bajo en hotelería.
Reglas de aprendizaje Ajuste por resultados históricos. Recalibrar umbral si acción no funciona.

6. Biblioteca inicial de reglas FARO

Una base seria debería tener:

150 a 300 reglas iniciales

Escalable a:

1.000+ reglas

Distribución sugerida:

Familia Cantidad inicial Escalable
Validación de datos 30-60 200+
Calidad de datos 20-40 150+
KPIs y umbrales 40-80 300+
Alertas 30-60 250+
Tensiones 30-80 300+
Acciones 30-60 300+
Responsables / RACI 20-40 150+
Escalamiento 20-40 100+
FARO Score 20-40 150+
Industria específica 50-150 500+

No conviene arrancar con 1.000 reglas activas. Conviene arrancar con pocas, críticas y bien probadas. Una regla mal hecha escala errores con una eficiencia admirable. Y eso no es precisamente virtud.


7. Estructura estándar de una regla FARO

Cada regla debería tener una ficha formal.

{
  "rule_code": "RULE_COMMERCIAL_001",
  "name": "Descuento alto con margen bajo",
  "description": "Detecta operaciones comerciales donde el descuento aplicado erosiona el margen bruto.",
  "module": "Comercial",
  "area": "Comercial",
  "industry_scope": ["construction_supplies", "retail", "manufacturing"],
  "inputs": [
    "gross_margin_rate",
    "discount_rate",
    "net_sales"
  ],
  "condition": {
    "all": [
      { "field": "gross_margin_rate", "operator": "<", "value": 0.22 },
      { "field": "discount_rate", "operator": ">", "value": 0.10 }
    ]
  },
  "confidence_min": 0.75,
  "severity": "alta",
  "then": {
    "alert": "margin_discount_alert",
    "possible_tension": "crecimiento_no_rentable",
    "suggested_action": "auditar_descuentos_altos",
    "responsible_role": "Gerente Comercial",
    "approver_role": "Dirección"
  },
  "score_impact": {
    "module": "Comercial",
    "points": -4
  },
  "active": true,
  "version": "1.0"
}

8. Campos obligatorios por regla

Campo Para qué sirve
rule_code Identificador único.
name Nombre claro.
description Qué detecta.
module Módulo FARO donde aplica.
area Área responsable.
industry_scope Industrias donde aplica.
inputs Variables necesarias.
condition Condición lógica.
confidence_min Confianza mínima requerida.
severity Severidad base.
then Consecuencia de la regla.
responsible_role Responsable sugerido.
score_impact Impacto en FARO Score.
version Versión de la regla.
active Si está activa o no.

9. Operadores lógicos

FARO debe soportar operadores básicos y avanzados.

Operador Uso Ejemplo
> Mayor que margen > 25%
< Menor que margen < 20%
>= Mayor o igual cumplimiento >= 90%
<= Menor o igual descuento <= 6%
== Igual estado == vencida
!= Distinto estado != cerrado
between Entre valores margen entre 20% y 25%
in Está dentro de lista área in comercial, finanzas
not_in No está en lista estado no en cerrado
and Todas las condiciones margen bajo y descuento alto
or Alguna condición stock bajo o cobertura insuficiente
not Negación no tiene responsable
trend_down Tendencia negativa margen baja 3 períodos
trend_up Tendencia positiva mora sube 4 semanas
change_gt Variación mayor a ventas suben más de 15%
exists Existe dato cliente existe
missing Falta dato costo faltante

10. Reglas de validación de datos

Estas reglas determinan si un dato puede ingresar o no al flujo.

10.1 Venta sin campos mínimos

def regla_validar_venta(venta):
    errores = []

    campos_obligatorios = [
        "fecha",
        "cliente_id",
        "producto_id",
        "cantidad",
        "precio_final",
        "vendedor_id",
        "sucursal_id"
    ]

    for campo in campos_obligatorios:
        if not venta.get(campo):
            errores.append(f"{campo}_faltante")

    if venta.get("cantidad", 0) <= 0:
        errores.append("cantidad_invalida")

    if venta.get("precio_final", 0) <= 0:
        errores.append("precio_invalido")

    return {
        "valid": len(errores) == 0,
        "errors": errores
    }

10.2 Acción sin responsable

def regla_accion_sin_responsable(accion):
    if not accion.get("responsable_id"):
        return {
            "rule": "ACTION_WITHOUT_OWNER",
            "status": "blocked",
            "message": "La acción no puede avanzar sin responsable asignado."
        }

    return None

11. Reglas de calidad de datos

Estas reglas no siempre bloquean, pero reducen confianza.

11.1 Costo faltante

def regla_costo_faltante(registros_sin_costo, registros_totales):
    if registros_totales == 0:
        return None

    ratio = registros_sin_costo / registros_totales

    if ratio > 0.15:
        return {
            "rule": "COST_MISSING_HIGH",
            "severity": "alta",
            "impact": "margen_no_confiable",
            "requires_validation": True
        }

    return None

11.2 Stock desactualizado

from datetime import datetime

def regla_stock_desactualizado(fecha_actualizacion, dias_maximos=2):
    dias = (datetime.now().date() - fecha_actualizacion).days

    if dias > dias_maximos:
        return {
            "rule": "STOCK_OUTDATED",
            "severity": "alta",
            "message": "El stock está desactualizado y no debería activar decisiones críticas."
        }

    return None

12. Reglas comerciales

12.1 Descuento alto con margen bajo

def regla_descuento_alto_margen_bajo(margen, descuento):
    if margen < 0.22 and descuento > 0.10:
        return {
            "alerta": "margen_critico_con_descuento_alto",
            "tension_posible": "crecimiento_no_rentable",
            "responsable": "Gerente Comercial",
            "accion": "auditar_descuentos_altos",
            "severidad": "alta"
        }

    return None

12.2 Ventas suben, margen baja

def regla_crecimiento_no_rentable_basico(ventas_var, margen_var):
    if ventas_var > 0.15 and margen_var < -0.05:
        return {
            "senal": "crecimiento_riesgoso",
            "tension_posible": "crecimiento_no_rentable",
            "accion": "analizar_descuentos_costos_y_mix",
            "severidad": "alta"
        }

    return None

12.3 Comisión desalineada

def regla_comision_desalineada(comision_var, margen_var, cobranza_var):
    condiciones = [
        comision_var > 0,
        margen_var < 0,
        cobranza_var > 0
    ]

    if sum(condiciones) >= 2:
        return {
            "tension": "comision_desalineada",
            "responsable": "Comercial / RRHH",
            "consultados": ["Finanzas"],
            "accion": "redisenar_formula_comision",
            "severidad": "alta"
        }

    return None

13. Reglas financieras

13.1 Caja débil con ventas altas

def regla_caja_debil_con_ventas_altas(ventas_var, caja_var, dias_cobranza_var):
    if ventas_var > 0.10 and caja_var < 0 and dias_cobranza_var > 7:
        return {
            "tension": "caja_debil_con_ventas_altas",
            "responsable": "Finanzas",
            "consultados": ["Comercial"],
            "accion": "priorizar_cobranza_clientes_grandes",
            "severidad": "alta"
        }

    return None

13.2 Gasto crece más que ventas

def regla_gasto_desalineado(ventas_var, gastos_var):
    if gastos_var > ventas_var and gastos_var > 0.10:
        return {
            "alerta": "gastos_crecen_mas_que_ventas",
            "tension": "estructura_sobredimensionada",
            "responsable": "Finanzas",
            "accion": "revisar_gastos_por_area",
            "severidad": "media_alta"
        }

    return None

13.3 Cliente grande moroso

def regla_cliente_grande_moroso(concentracion_cliente, dias_mora, margen_cliente):
    if concentracion_cliente > 0.15 and dias_mora > 30 and margen_cliente < 0.20:
        return {
            "tension": "cliente_grande_riesgoso",
            "responsable": "Finanzas",
            "consultados": ["Comercial"],
            "accion": "condicionar_credito_o_exigir_pago_parcial",
            "severidad": "alta"
        }

    return None

14. Reglas de stock

14.1 Stock crítico

def regla_stock_critico(stock_actual, stock_minimo):
    if stock_actual < stock_minimo:
        return {
            "alerta": "stock_bajo_minimo",
            "tension_posible": "stock_critico_comercial",
            "responsable": "Stock",
            "consultados": ["Compras", "Comercial"],
            "accion": "activar_reposicion",
            "severidad": "alta"
        }

    return None

14.2 Cobertura menor al plazo proveedor

def regla_cobertura_menor_plazo(stock_actual, venta_promedio_diaria, plazo_proveedor):
    if venta_promedio_diaria == 0:
        return None

    dias_cobertura = stock_actual / venta_promedio_diaria

    if dias_cobertura < plazo_proveedor:
        return {
            "alerta": "cobertura_insuficiente",
            "tension": "stock_critico_comercial",
            "responsable": "Compras",
            "consultados": ["Stock", "Comercial"],
            "accion": "comprar_o_buscar_proveedor_alternativo",
            "dias_cobertura": dias_cobertura,
            "severidad": "alta"
        }

    return None

14.3 Stock mal compuesto

def regla_stock_mal_compuesto(stock_total_var, rotacion_var, quiebres_productos_clave, stock_inmovilizado_var):
    condiciones = [
        stock_total_var > 0.10,
        rotacion_var < -0.10,
        quiebres_productos_clave > 0,
        stock_inmovilizado_var > 0.15
    ]

    if sum(condiciones) >= 3:
        return {
            "tension": "stock_mal_compuesto",
            "responsable": "Stock",
            "consultados": ["Compras", "Comercial", "Finanzas"],
            "accion": "revisar_mix_stock_y_bloquear_compras_lentas",
            "severidad": "alta"
        }

    return None

15. Reglas de compras y proveedores

15.1 Proveedor crítico

def regla_proveedor_critico(cumplimiento, dependencia, plazo_var):
    if cumplimiento < 0.75 and dependencia > 0.40:
        return {
            "tension": "proveedor_critico",
            "responsable": "Compras",
            "consultados": ["Stock", "Finanzas"],
            "accion": "buscar_proveedor_alternativo",
            "severidad": "alta"
        }

    if plazo_var > 0.25 and dependencia > 0.50:
        return {
            "alerta": "plazo_proveedor_en_deterioro",
            "tension_posible": "proveedor_critico",
            "accion": "renegociar_plazo_o_abrir_alternativa",
            "severidad": "media_alta"
        }

    return None

15.2 Compras reactivas

def regla_compras_reactivas(compras_urgentes_ratio, variacion_precio_compra):
    if compras_urgentes_ratio > 0.20 and variacion_precio_compra > 0.10:
        return {
            "tension": "compras_reactivas",
            "responsable": "Compras",
            "consultados": ["Stock", "Finanzas"],
            "accion": "crear_plan_reposicion_por_rotacion",
            "severidad": "media_alta"
        }

    return None

16. Reglas de RRHH

16.1 Costo laboral desalineado

def regla_costo_laboral_desalineado(costo_laboral_var, ventas_var, productividad_var):
    if costo_laboral_var > ventas_var and productividad_var <= 0:
        return {
            "tension": "costo_laboral_desalineado",
            "responsable": "RRHH",
            "consultados": ["Finanzas", "Dirección"],
            "accion": "revisar_dotacion_y_productividad",
            "severidad": "media_alta"
        }

    return None

16.2 Ausentismo operativo

def regla_ausentismo_operativo(ausentismo, demoras_operativas_var):
    if ausentismo > 0.08 and demoras_operativas_var > 0:
        return {
            "tension": "ausentismo_operativo",
            "responsable": "RRHH",
            "consultados": ["Operaciones"],
            "accion": "revisar_cobertura_operativa_y_plan_de_reemplazos",
            "severidad": "media"
        }

    return None

17. Reglas de operaciones y workflow

17.1 Acción crítica vencida

def regla_accion_critica_vencida(accion):
    if accion["prioridad"] == "critica" and accion["estado"] != "cerrada" and accion["dias_vencida"] >= 1:
        return {
            "alerta": "accion_critica_vencida",
            "responsable": accion["responsable"],
            "escalar_a": "Gerencia General",
            "accion": "exigir_cierre_o_reasignar",
            "severidad": "critica"
        }

    return None

17.2 Dirección sin ejecución

def regla_direccion_sin_ejecucion(decisiones, acciones_creadas, acciones_vencidas, reincidencias):
    if decisiones == 0:
        return None

    ratio_acciones = acciones_creadas / decisiones
    ratio_vencidas = acciones_vencidas / acciones_creadas if acciones_creadas else 0

    condiciones = [
        ratio_acciones < 0.70,
        ratio_vencidas > 0.20,
        reincidencias > 2
    ]

    if sum(condiciones) >= 2:
        return {
            "tension": "direccion_sin_ejecucion",
            "responsable": "Gerencia General",
            "accion": "activar_workflow_obligatorio_y_escalamiento",
            "severidad": "alta"
        }

    return None

18. Reglas de alertas FARO

Una regla puede emitir una alerta cuando el evento necesita visibilidad.

Ejemplo de alerta simple

def generar_alerta(rule_result):
    if not rule_result:
        return None

    return {
        "alert_code": rule_result.get("alerta") or rule_result.get("tension"),
        "severity": rule_result.get("severidad", "media"),
        "responsible": rule_result.get("responsable"),
        "suggested_action": rule_result.get("accion"),
        "status": "open"
    }

Ejemplo de regla con confianza mínima

def evaluar_regla_con_confianza(condicion_cumplida, confianza, confianza_minima=0.75):
    if condicion_cumplida and confianza >= confianza_minima:
        return "activar_alerta"

    if condicion_cumplida and confianza < confianza_minima:
        return "observar_requiere_validacion"

    return "sin_accion"

Esto es clave: FARO no debe gritar con datos flojos.


19. Reglas de tensiones

Las tensiones nacen de varias señales o reglas activas.

19.1 Crecimiento no rentable

def regla_tension_crecimiento_no_rentable(ctx):
    condiciones = [
        ctx["ventas_var"] > 0.15,
        ctx["margen_var"] < -0.05,
        ctx["descuento_var"] > 0.04,
        ctx["dias_cobranza_var"] > 7,
        ctx["comision_var"] > 0
    ]

    if sum(condiciones) >= 3:
        return {
            "tension": "crecimiento_no_rentable",
            "areas": ["Comercial", "Finanzas", "Stock", "RRHH"],
            "severidad": "alta",
            "responsable": "Gerente Comercial",
            "consultados": ["Finanzas", "Stock", "RRHH"],
            "acciones": [
                "auditar_descuentos_altos",
                "revisar_cobranza_clientes_grandes",
                "revisar_formula_comision"
            ]
        }

    return None

19.2 Cliente grande riesgoso

def regla_tension_cliente_grande_riesgoso(ctx):
    condiciones = [
        ctx["concentracion_cliente"] > 0.15,
        ctx["dias_mora"] > 30,
        ctx["margen_cliente"] < 0.20,
        ctx["deuda_vencida"] > 0
    ]

    if sum(condiciones) >= 3:
        return {
            "tension": "cliente_grande_riesgoso",
            "responsable": "Finanzas",
            "consultados": ["Comercial"],
            "accion": "revisar_limite_credito_y_condiciones",
            "severidad": "alta"
        }

    return None

20. Reglas de asignación de responsables

FARO debe asignar responsables según RACI.

Ejemplo

def asignar_responsable_por_tipo_evento(tipo_evento):
    mapa = {
        "margen_critico": {
            "R": "Gerente Comercial",
            "A": "Dirección",
            "C": ["Finanzas", "Compras"],
            "I": ["Administración"]
        },
        "stock_critico": {
            "R": "Responsable de Stock",
            "A": "Dirección",
            "C": ["Compras", "Comercial"],
            "I": ["Sucursal"]
        },
        "cobranza_lenta": {
            "R": "Finanzas",
            "A": "Dirección",
            "C": ["Comercial"],
            "I": ["Administración"]
        },
        "accion_vencida": {
            "R": "Responsable asignado",
            "A": "Gerencia General",
            "C": ["Área involucrada"],
            "I": ["Dirección"]
        }
    }

    return mapa.get(tipo_evento)

Regla sana:

Una alerta sin responsable es ruido con diseño premium.


21. Reglas de escalamiento

FARO debe escalar cuando una acción no se resuelve.

Escalamiento por vencimiento

def regla_escalamiento_accion(accion):
    if accion["estado"] == "cerrada":
        return None

    if accion["prioridad"] == "critica" and accion["dias_vencida"] >= 1:
        return {
            "escalar_a": "Gerencia General",
            "motivo": "acción crítica vencida",
            "severidad": "critica"
        }

    if accion["prioridad"] == "alta" and accion["dias_vencida"] >= 3:
        return {
            "escalar_a": "Dirección",
            "motivo": "acción alta vencida más de 3 días",
            "severidad": "alta"
        }

    if accion["dias_vencida"] >= 7:
        return {
            "escalar_a": "Responsable superior",
            "motivo": "acción vencida recurrente",
            "severidad": "media"
        }

    return None

22. Reglas de FARO Score

Las reglas también modifican el Score.

Ejemplo simple

def impacto_score_por_evento(evento):
    impactos = {
        "margen_critico": -5,
        "stock_critico": -4,
        "cobranza_roja": -5,
        "accion_critica_vencida": -6,
        "crecimiento_no_rentable": -8,
        "calidad_datos_baja": -3,
        "accion_cerrada_en_fecha": 2,
        "margen_recuperado": 4
    }

    return impactos.get(evento, 0)

Regla con severidad

def impacto_score_por_severidad(severidad):
    if severidad == "critica":
        return -8
    if severidad == "alta":
        return -5
    if severidad == "media":
        return -2
    if severidad == "baja":
        return -1
    return 0

23. Reglas por industria

23.1 Construcción / insumos

Canje mal evaluado

def regla_canje_mal_evaluado(valor_materiales, valor_activo_recibido, liquidez_activo, margen_estimado):
    condiciones = [
        valor_activo_recibido < valor_materiales,
        liquidez_activo < 0.50,
        margen_estimado < 0.20
    ]

    if sum(condiciones) >= 2:
        return {
            "tension": "canje_mal_evaluado",
            "responsable": "Dirección / Finanzas",
            "consultados": ["Comercial", "Legal"],
            "accion": "evaluar_canje_con_modelo_financiero",
            "severidad": "alta"
        }

    return None

Referido sin trazabilidad

def regla_referido_sin_trazabilidad(comision_referido, margen_operacion, referido_identificado):
    if comision_referido > 0 and not referido_identificado:
        return {
            "alerta": "referido_sin_trazabilidad",
            "responsable": "Comercial",
            "accion": "regularizar_identificacion_y_condicion_del_referido",
            "severidad": "media_alta"
        }

    if margen_operacion > 0 and (comision_referido / margen_operacion) > 0.25:
        return {
            "tension": "referido_erosiona_margen",
            "accion": "revisar_politica_de_referidos",
            "severidad": "alta"
        }

    return None

23.2 Retail

def regla_promocion_destructiva(ventas_var, margen_var, promo_activa):
    if promo_activa and ventas_var > 0.10 and margen_var < -0.08:
        return {
            "tension": "promocion_destructiva",
            "responsable": "Comercial / Marketing",
            "accion": "revisar_promocion_por_categoria_y_margen",
            "severidad": "alta"
        }

    return None

23.3 Logística

def regla_ruta_no_rentable(costo_km, costo_km_objetivo, margen_cliente):
    if costo_km > costo_km_objetivo * 1.10 and margen_cliente < 0.12:
        return {
            "tension": "ruta_no_rentable",
            "responsable": "Operaciones",
            "consultados": ["Finanzas"],
            "accion": "redisenar_ruta_o_renegociar_tarifa",
            "severidad": "alta"
        }

    return None

23.4 Hotelería

def regla_ocupacion_alta_tarifa_baja(ocupacion, adr, adr_objetivo):
    if ocupacion > 0.85 and adr < adr_objetivo * 0.90:
        return {
            "tension": "ocupacion_alta_tarifa_baja",
            "responsable": "Revenue Management",
            "accion": "recalibrar_tarifas_y_canales",
            "severidad": "media_alta"
        }

    return None

24. Motor de reglas FARO

El motor de reglas debe evaluar condiciones y devolver consecuencias.

Flujo:

Datos / KPIs / Señales
→ seleccionar reglas aplicables
→ validar calidad mínima
→ evaluar condiciones
→ calcular severidad
→ calcular confianza
→ generar alerta / tensión / acción
→ asignar responsable
→ impactar score
→ registrar trazabilidad

Código conceptual:

def motor_reglas(contexto, reglas):
    resultados = []

    for regla in reglas:
        if not regla.get("active", True):
            continue

        if contexto.get("confidence", 1) < regla.get("confidence_min", 0):
            resultados.append({
                "rule_code": regla["rule_code"],
                "status": "observed_low_confidence"
            })
            continue

        if evaluar_condicion(regla["condition"], contexto):
            resultados.append({
                "rule_code": regla["rule_code"],
                "status": "triggered",
                "then": regla["then"],
                "severity": regla.get("severity"),
                "score_impact": regla.get("score_impact")
            })

    return resultados

25. Evaluador simple de condiciones

Ejemplo básico:

def comparar(valor, operador, esperado):
    if operador == ">":
        return valor > esperado
    if operador == "<":
        return valor < esperado
    if operador == ">=":
        return valor >= esperado
    if operador == "<=":
        return valor <= esperado
    if operador == "==":
        return valor == esperado
    if operador == "!=":
        return valor != esperado
    return False


def evaluar_condicion(condicion, contexto):
    if "all" in condicion:
        return all(
            comparar(
                contexto.get(c["field"]),
                c["operator"],
                c["value"]
            )
            for c in condicion["all"]
        )

    if "any" in condicion:
        return any(
            comparar(
                contexto.get(c["field"]),
                c["operator"],
                c["value"]
            )
            for c in condicion["any"]
        )

    return False

26. Tabla SQL de reglas

CREATE TABLE business_rules (
    rule_code TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    description TEXT,
    module TEXT,
    area_id TEXT,
    industry_scope JSONB,
    inputs JSONB,
    condition JSONB NOT NULL,
    confidence_min NUMERIC DEFAULT 0.70,
    severity TEXT,
    then_action JSONB,
    score_impact JSONB,
    responsible_role TEXT,
    approver_role TEXT,
    active BOOLEAN DEFAULT true,
    version TEXT DEFAULT '1.0',
    created_by TEXT,
    approved_by TEXT,
    created_at TIMESTAMP DEFAULT now(),
    updated_at TIMESTAMP DEFAULT now()
);

27. Tabla SQL de ejecución de reglas

CREATE TABLE rule_executions (
    execution_id TEXT PRIMARY KEY,
    rule_code TEXT NOT NULL,
    company_id TEXT,
    branch_id TEXT,
    area_id TEXT,
    input_snapshot JSONB,
    result JSONB,
    status TEXT,
    severity TEXT,
    confidence NUMERIC,
    triggered_at TIMESTAMP DEFAULT now()
);

Esto permite auditar:

qué regla se ejecutó,
con qué datos,
qué resultado dio,
qué alerta generó,
qué acción disparó,
qué score impactó.

28. Versionado de reglas

Las reglas cambian. FARO debe versionarlas.

Ejemplo:

Regla margen v1:
margen < 20%

Regla margen v2:
margen < 22% y descuento > 10%

Regla margen v3:
margen depende de industria y familia de producto.

Tabla sugerida:

CREATE TABLE business_rule_versions (
    version_id TEXT PRIMARY KEY,
    rule_code TEXT NOT NULL,
    version TEXT NOT NULL,
    previous_definition JSONB,
    new_definition JSONB,
    changed_by TEXT,
    approved_by TEXT,
    change_reason TEXT,
    active_from DATE,
    created_at TIMESTAMP DEFAULT now()
);

Regla sana:

Una regla crítica no se cambia sin motivo, aprobador y fecha de vigencia.


29. Gobernanza de reglas

Tipo de regla Responsable Aprobador
Validación de datos Sistemas / Data Owner Dirección / Data Governance
Calidad de datos Sistemas / Data Owner Dirección
Comercial Gerente Comercial Dirección
Finanzas Finanzas Dirección
Stock Stock / Compras Dirección
RRHH RRHH Dirección
Workflow Gerencia General Dirección
FARO Score Dirección Directorio
Industria Consultoría / Dirección Dirección / Cliente

30. Estados de una regla

Estado Significado
Draft Regla en diseño.
Testing Regla en prueba.
Active Regla activa.
Observed Regla observa, pero no dispara acción automática.
Suspended Regla pausada.
Deprecated Regla reemplazada.
Archived Regla histórica.

31. Reglas en modo observación

Al principio, muchas reglas deberían correr en modo observación.

Ejemplo:

La regla detecta margen bajo,
pero todavía no genera acción automática.
Primero se valida si la detección es correcta.

Esto evita automatizar errores.

Recomendación:

Fase inicial:
reglas críticas en observación durante 2 a 4 semanas.

Luego:
activar alertas y acciones cuando se confirme que funcionan.

32. Reglas y confianza

Una misma regla puede tener distintos resultados según confianza.

Condición Resultado
Regla cumplida + confianza alta Activar alerta / acción.
Regla cumplida + confianza media Activar alerta con observación.
Regla cumplida + confianza baja Pedir validación.
Regla no cumplida No actuar.

Ejemplo:

def decidir_salida_regla(regla_cumplida, confianza):
    if not regla_cumplida:
        return "sin_accion"

    if confianza >= 0.85:
        return "activar_accion"

    if confianza >= 0.70:
        return "activar_alerta_con_observacion"

    if confianza >= 0.50:
        return "pedir_validacion"

    return "no_usar_para_decision"

33. Reglas y acciones automáticas

No todas las reglas deben generar acciones automáticas.

Tipo de evento Acción automática
Dato inválido Sí, bloquear o observar.
Tarea vencida Sí, notificar o escalar.
Stock bajo mínimo Sí, crear acción de revisión.
Margen bajo Mejor sugerir, no ejecutar automáticamente.
Cliente moroso Sugerir bloqueo, requiere aprobación.
Cambio de comisión Nunca automático sin Dirección.
Canje Nunca automático sin análisis directivo.
Despido / sanción RRHH Nunca automático. Solo señal / recomendación.

Regla conservadora:

FARO puede sugerir muchas decisiones, pero no debe ejecutar decisiones sensibles sin aprobación humana.


34. Ejemplo completo: regla comercial

Contexto

{
  "ventas_var": 0.18,
  "margen_actual": 0.21,
  "margen_anterior": 0.28,
  "descuento_actual": 0.12,
  "dias_cobranza_var": 10,
  "confidence": 0.84
}

Regla

Si ventas suben más de 15%
y margen cae más de 5 puntos
y descuento supera 10%
entonces detectar crecimiento no rentable.

Resultado FARO

{
  "tension": "crecimiento_no_rentable",
  "severity": "alta",
  "confidence": 0.84,
  "responsible": "Gerente Comercial",
  "consulted": ["Finanzas", "Stock", "RRHH"],
  "suggested_actions": [
    "auditar_descuentos_altos",
    "revisar_comisiones",
    "priorizar_cobranza_clientes_grandes"
  ],
  "score_impact": -8
}

35. Ejemplo completo: regla de stock

Contexto

{
  "product_id": "PROD_001",
  "stock_actual": 180,
  "stock_minimo": 150,
  "venta_promedio_diaria": 45,
  "plazo_proveedor": 7,
  "confidence": 0.90
}

Regla

Si días de cobertura < plazo proveedor,
aunque stock actual sea mayor al mínimo,
activar señal temprana de stock crítico futuro.

Resultado

{
  "signal": "stock_critico_futuro",
  "dias_cobertura": 4,
  "plazo_proveedor": 7,
  "alert": "cobertura_insuficiente",
  "responsible": "Compras",
  "consulted": ["Stock", "Comercial"],
  "suggested_action": "anticipar_reposicion",
  "severity": "alta"
}

36. Ejemplo completo: regla de dirección

Contexto

{
  "decisiones_tomadas": 20,
  "acciones_creadas": 12,
  "acciones_vencidas": 6,
  "problemas_reincidentes": 4,
  "confidence": 0.88
}

Resultado FARO

{
  "tension": "direccion_sin_ejecucion",
  "severity": "alta",
  "responsible": "Gerencia General",
  "suggested_action": "activar_workflow_obligatorio",
  "score_impact": -7
}

37. Herramientas posibles

Necesidad Herramientas
Motor de reglas propio Python, Node.js, TypeScript
Reglas JSON JSON Rules Engine, json-logic
Backend FastAPI, NestJS, Django
Base de reglas PostgreSQL JSONB
Orquestación Celery, Temporal, Airflow, Prefect
Testing de reglas Pytest, unit tests, fixtures
Auditoría rule_executions, logs, data lineage
Versionado Git + tablas de versiones
Reglas complejas Drools, Durable Rules, motores expertos
IA explicativa OpenAI API + plantillas FARO + RAG
Simulación Python, Pandas, Monte Carlo, escenarios

38. Testing de reglas

Cada regla debe probarse con casos.

Ejemplo test simple

def test_descuento_alto_margen_bajo():
    resultado = regla_descuento_alto_margen_bajo(
        margen=0.18,
        descuento=0.12
    )

    assert resultado is not None
    assert resultado["alerta"] == "margen_critico_con_descuento_alto"
    assert resultado["severidad"] == "alta"

Test negativo

def test_no_dispara_si_margen_sano():
    resultado = regla_descuento_alto_margen_bajo(
        margen=0.30,
        descuento=0.12
    )

    assert resultado is None

Sin testing, las reglas son opiniones con sintaxis.


39. Riesgos si no existe esta capa

Riesgo Consecuencia
KPIs sin acción El sistema informa, pero no dirige.
Alertas arbitrarias El usuario no entiende por qué se activan.
Tensiones débiles No hay lógica que las sostenga.
Recomendaciones genéricas FARO dice obviedades.
Score inexplicable No se sabe qué lo afecta.
Automatización peligrosa Se toman decisiones con reglas mal definidas.
Baja confianza técnica Un socio técnico puede cuestionar la arquitectura.
Difícil escalar Cada cliente requiere lógica manual.

40. Output final del Anexo 20

Al finalizar este anexo, FARO debe tener definido:

1. Biblioteca inicial de reglas de negocio.
2. Tipos de reglas FARO.
3. Reglas de validación de datos.
4. Reglas de calidad de datos.
5. Reglas por KPI.
6. Reglas por señal.
7. Reglas de alerta.
8. Reglas de tensión.
9. Reglas de acción.
10. Reglas de responsable / RACI.
11. Reglas de escalamiento.
12. Reglas de FARO Score.
13. Reglas por industria.
14. Motor de reglas.
15. Estructura JSON de reglas.
16. Modelo SQL de reglas.
17. Tabla de ejecución de reglas.
18. Versionado de reglas.
19. Testing de reglas.
20. Gobernanza y aprobadores.
21. Modo observación.
22. Reglas automáticas vs reglas con aprobación humana.
23. Relación regla → alerta.
24. Relación regla → tensión.
25. Relación regla → acción.
26. Relación regla → score.

41. Conexión con otros anexos

Próximo anexo Qué recibe desde Anexo 20
Anexo 17 — Biblioteca de KPIs KPIs usados como inputs de reglas.
Anexo 18 — Objetivos y umbrales Condiciones para definir estados.
Anexo 19 — Señales FARO Señales que alimentan reglas.
Anexo 21 — Alertas FARO Reglas que disparan alertas.
Anexo 22 — Biblioteca de tensiones Combinaciones de reglas y señales.
Anexo 23 — Diagnóstico ejecutivo Interpretación de reglas activadas.
Anexo 24 — Confianza del diagnóstico Confianza mínima para actuar.
Anexo 25 — Priorización ejecutiva Severidad y prioridad según reglas.
Anexo 26 — Recomendaciones FARO Recomendaciones derivadas de reglas.
Anexo 29 — Biblioteca de acciones Acciones sugeridas por regla.
Anexo 31 — Workflow y escalamiento Reglas que asignan, vencen y escalan acciones.
Anexo 35 — FARO Score Penalizaciones y mejoras por reglas activadas.
Anexo 37Recalibración Ajuste de reglas según resultados reales.

Las reglas de negocio FARO son la capa lógica que convierte KPIs y señales en decisiones accionables. Definen cuándo un dato importa, cuándo se activa una alerta, cuándo nace una tensión, qué acción se sugiere, quién responde, cuándo se escala y cómo impacta en el FARO Score.

Versión 1.0 · Última revisión: 2026-05-28 Anexo 20 de 40 · Fase 6.1