Artículos

Inventario al cierre en Power BI: patrón avanzado DAX para calcular la última observación

7 noviembre, 2026 · 12 min de lectura
0
(0)

El inventario es una de las métricas más delicadas de modelar en Power BI. A diferencia de las ventas, no debemos sumar sus valores a lo largo del tiempo: el inventario de cierre de un periodo suele ser el último saldo disponible hasta la fecha de corte. En este tutorial veremos cómo resolverlo con DAX, cómo mantener los filtros de producto y tienda, y cómo evitar errores en los totales.

Nivel: avanzado · Temática: DAX, contexto de filtro, contexto de fila y medidas semi-aditivas.

1. Objetivo y escenario de negocio

Imagina una empresa que registra periódicamente el inventario de sus productos en distintas tiendas. Necesita responder a preguntas como:

  • ¿Cuántas unidades había al cierre del periodo seleccionado?
  • ¿Qué ocurre si un producto no tiene registro exactamente en la fecha de cierre?
  • ¿Cómo se calcula correctamente el total de varias tiendas y productos?
  • ¿Cómo evitamos sumar el inventario de todos los días del periodo?

El inventario es una magnitud semi-aditiva: normalmente podemos sumar sus valores entre productos y tiendas en una misma fecha, pero no debemos sumarlos entre fechas. Si un producto tiene 100 unidades el lunes y 120 el martes, el inventario al cierre del martes es 120, no 220.

Construiremos dos alternativas: (1) última observación registrada hasta la fecha de corte, calculada para cada combinación de producto y tienda; y (2) inventario registrado exactamente en la fecha de corte.

2. Modelo de datos mínimo

Usaremos cuatro tablas. La tabla de hechos contiene una fila por combinación de fecha, producto y tienda. Si tu origen contiene varias filas para esa combinación, asumiremos que representan registros o movimientos que deben agregarse.

DimFecha

FechaAñoMes
01/11/20262026Noviembre
02/11/20262026Noviembre
03/11/20262026Noviembre
04/11/20262026Noviembre

DimProducto

ProductoKeyProducto
1Portátil
2Monitor

DimTienda

TiendaKeyTienda
10Madrid
20Valencia

FactInventario

FechaProductoKeyTiendaKeyUnidades
01/11/2026110100
03/11/2026110120
02/11/202621050
03/11/202612080
04/11/202622060

Relaciones recomendadas, con dirección de filtro desde las dimensiones hacia la tabla de hechos:

  • DimFecha[Fecha] → FactInventario[Fecha]
  • DimProducto[ProductoKey] → FactInventario[ProductoKey]
  • DimTienda[TiendaKey] → FactInventario[TiendaKey]

Asegúrate de que las columnas relacionadas tengan tipos compatibles. Si la fecha de la tabla de hechos contiene hora, crea una columna de fecha sin componente horario para relacionarla correctamente con la dimensión calendario.

3. Medida base

Empezamos con una medida sencilla:

Unidades Inventario :=
SUM ( FactInventario[Unidades] )

Esta medida responde al contexto de filtro actual. En una fila de producto calcula las unidades de ese producto; en una fila de tienda, las de esa tienda; y en el total general suma todas las filas visibles. El problema aparece cuando seleccionamos varios días: la medida suma las observaciones de todos ellos, algo que no representa el inventario al cierre.

4. Solución principal: última observación por producto y tienda

Queremos que cada combinación de producto y tienda utilice su última fecha de inventario registrada, siempre que sea anterior o igual a la fecha de corte.

Paso 1. Obtener la fecha de corte

Fecha Corte :=
MAX ( DimFecha[Fecha] )

Si el usuario selecciona del 1 al 4 de noviembre, la fecha de corte será el 4 de noviembre. Si selecciona únicamente el 3 de noviembre, será el 3 de noviembre. Esta definición supone que el negocio interpreta una selección de varias fechas como un periodo cuyo cierre es la fecha máxima visible.

Paso 2. Calcular la última fecha disponible

Ultima Fecha Inventario :=
VAR FechaCorte =
    MAX ( DimFecha[Fecha] )
RETURN
    CALCULATE (
        MAX ( FactInventario[Fecha] ),
        REMOVEFILTERS ( DimFecha ),
        FactInventario[Fecha] <= FechaCorte
    )

¿Qué hace cada parte?

  1. FechaCorte captura el límite temporal del contexto original.
  2. REMOVEFILTERS(DimFecha) elimina los filtros de fecha para poder buscar en todo el historial.
  3. FactInventario[Fecha] <= FechaCorte impide recuperar registros posteriores al cierre.
  4. MAX devuelve la fecha más reciente que cumple la condición.

Los filtros de producto, tienda y otras dimensiones se mantienen. Por eso, la fecha encontrada puede ser distinta para cada producto y tienda. Es importante que la variable FechaCorte se capture antes de quitar los filtros de calendario.

Paso 3. Recuperar las unidades de esa fecha

Inventario Ultima Observacion :=
VAR FechaInventario =
    [Ultima Fecha Inventario]
RETURN
    IF (
        ISBLANK ( FechaInventario ),
        BLANK (),
        CALCULATE (
            [Unidades Inventario],
            REMOVEFILTERS ( DimFecha ),
            FactInventario[Fecha] = FechaInventario
        )
    )

La medida busca las unidades de la última fecha encontrada. Eliminamos el filtro de fecha antes de aplicar la fecha concreta de inventario para que el filtro temporal original no impida recuperar una observación anterior.

5. El detalle decisivo: calcular correctamente los totales

Una medida que funciona en una fila de producto puede dar un resultado incorrecto en el total. Power BI no suma necesariamente los resultados visibles: vuelve a evaluar la expresión en el contexto del total.

En este caso queremos sumar la última observación de cada combinación de producto y tienda, no buscar una única última fecha para todo el conjunto. Para ello iteramos por las combinaciones visibles:

Inventario al Cierre :=
VAR FechaCorte =
    MAX ( DimFecha[Fecha] )

VAR Combinaciones =
    SUMMARIZECOLUMNS (
        DimProducto[ProductoKey],
        DimTienda[TiendaKey]
    )

RETURN
    SUMX (
        Combinaciones,
        VAR UltimaFecha =
            CALCULATE (
                MAX ( FactInventario[Fecha] ),
                REMOVEFILTERS ( DimFecha ),
                FactInventario[Fecha] <= FechaCorte
            )
        RETURN
            IF (
                ISBLANK ( UltimaFecha ),
                BLANK (),
                CALCULATE (
                    [Unidades Inventario],
                    REMOVEFILTERS ( DimFecha ),
                    FactInventario[Fecha] = UltimaFecha
                )
            )
    )

¿Cómo se evalúa?

  1. Construcción de combinaciones. SUMMARIZECOLUMNS obtiene las combinaciones de producto y tienda visibles según los filtros activos en esas dimensiones. Al no incluir una expresión de medida que elimine combinaciones vacías, puede generar el cruce de los valores visibles de ambas dimensiones.
  2. Iteración. SUMX recorre esas combinaciones y crea un contexto de fila para cada una.
  3. Transición de contexto. Los CALCULATE convierten el contexto de fila en contexto de filtro. Así, la última fecha y las unidades se calculan para el producto y tienda de la iteración actual.
  4. Agregación. SUMX suma las observaciones recuperadas para formar el total.

La iteración es lo que hace que el total sea coherente con la granularidad del inventario. Si el modelo tiene muchas combinaciones posibles, esta estrategia puede resultar costosa; más adelante veremos alternativas de optimización.

6. Cómo interpretar los resultados

Supongamos que seleccionamos del 1 al 4 de noviembre. La última observación por combinación sería:

ProductoTiendaÚltima fechaInventario
PortátilMadrid03/11120
MonitorMadrid02/1150
PortátilValencia03/1180
MonitorValencia04/1160
Total310

El total es 120 + 50 + 80 + 60 = 310. No se suman las observaciones del 1, 2, 3 y 4 de noviembre: se recupera una observación por cada combinación de producto y tienda.

7. Alternativa: inventario exactamente en la fecha de corte

Hay empresas que no quieren recuperar la última observación anterior. Necesitan conocer exclusivamente lo registrado en la fecha de corte. En ese caso podemos usar:

Inventario Fecha Exacta :=
VAR FechaCorte =
    MAX ( DimFecha[Fecha] )
RETURN
    CALCULATE (
        [Unidades Inventario],
        REMOVEFILTERS ( DimFecha ),
        DimFecha[Fecha] = FechaCorte
    )

Esta alternativa puede devolver BLANK() cuando no hay registro en la fecha de corte. La diferencia entre ambos enfoques es funcional, no solo técnica:

SituaciónÚltima observaciónFecha exacta
Hay registro el día de cierreRecupera ese registroRecupera ese registro
No hay registro el día de cierreRecupera uno anteriorBLANK
No existe ningún registro anteriorBLANKBLANK

Trade-off: la última observación ofrece continuidad operativa, pero puede mostrar datos antiguos. La fecha exacta refleja estrictamente lo registrado ese día, pero puede dejar huecos.

8. Casos límite y errores habituales

A. BLANK no es lo mismo que cero

Un inventario de cero unidades puede ser perfectamente válido. 0 significa que existe una observación que indica que no queda inventario; BLANK() indica que no hay una observación recuperable o que no existe información.

Si necesitas mostrar cero en una tarjeta cuando no hay datos, puedes crear una medida de presentación:

Inventario Cero si Sin Datos :=
COALESCE ( [Inventario al Cierre], 0 )

Puede ser apropiada para una tarjeta, pero no debería sustituir automáticamente a la medida base en cálculos posteriores: hacerlo puede confundir ausencia de información con inventario realmente igual a cero.

B. Selección de varios meses

Si se seleccionan noviembre y diciembre, MAX(DimFecha[Fecha]) establece como fecha de corte el último día visible de diciembre. La medida calcula el inventario a esa fecha, no el promedio de los cierres mensuales. Para comparar cierres mensuales, evalúa la medida por mes en una matriz o gráfico.

C. Filtros simultáneos

Los filtros de producto, tienda y otras dimensiones permanecen activos, salvo que se eliminen expresamente. En cambio, REMOVEFILTERS(DimFecha) elimina los filtros de toda la dimensión de fechas, no solo el de la columna Fecha. Esto importa cuando hay filtros simultáneos por año, trimestre, mes y día.

D. Una fecha futura en la selección

Si el usuario selecciona una fecha futura respecto al último registro de inventario, la medida principal recuperará la última observación anterior. Es correcto para un cálculo “a fecha de”, pero puede resultar engañoso si se interpreta como inventario actualizado. Conviene mostrar también la antigüedad del dato.

Dias desde Ultimo Inventario :=
VAR FechaCorte =
    MAX ( DimFecha[Fecha] )
VAR FechaUltimoDato =
    [Ultima Fecha Inventario]
RETURN
    IF (
        ISBLANK ( FechaUltimoDato ),
        BLANK (),
        DATEDIFF (
            FechaUltimoDato,
            FechaCorte,
            DAY
        )
    )

Así puedes identificar un inventario que lleva varios días sin actualizarse.

E. La última fecha existe, pero el inventario es BLANK

La medida busca la última fecha con registros en la tabla de hechos. Si una fila existe pero su cantidad es BLANK, la fecha podría considerarse válida y el resultado podría ser BLANK. Si el negocio define que las filas con cantidad BLANK no son observaciones válidas, añade esa condición a la búsqueda de la última fecha. No confundas una fila ausente con una fila cuyo valor es BLANK.

9. Rendimiento y validación con DAX Studio

La solución con SUMX es clara y robusta, pero su coste depende del número de combinaciones visibles de producto y tienda. Un informe con 10.000 productos y 300 tiendas podría implicar una iteración potencialmente grande.

Recomendaciones:

  • Mantén un esquema en estrella y relaciones unidireccionales cuando sea posible.
  • Evita iterar por combinaciones que no tengan relevancia para el cálculo.
  • Comprueba si la granularidad real del inventario permite reducir las combinaciones.
  • Si el proceso es muy costoso, considera preparar una tabla de cierres periódicos en el ETL o en Power Query.
  • No añadas ALL o REMOVEFILTERS de forma indiscriminada: pueden alterar tanto el resultado como el rendimiento.

Validación en DAX Studio

Ejecuta una consulta como esta para inspeccionar el resultado:

EVALUATE
SUMMARIZECOLUMNS (
    DimProducto[Producto],
    DimTienda[Tienda],
    "Ultima Fecha", [Ultima Fecha Inventario],
    "Inventario", [Inventario al Cierre]
)

Para revisar el rendimiento:

  • Activa Server Timings.
  • Activa Query Plan.
  • Ejecuta la consulta en un escenario con pocos productos y tiendas.
  • Repite con un contexto de filtro amplio.
  • Observa el tiempo de Storage Engine y Formula Engine, el número de consultas y las operaciones de iteración.

No hay un umbral universal de tiempo que determine si la medida es buena o mala: compara alternativas en el mismo modelo, con los mismos filtros y datos. No atribuyas mejoras de rendimiento sin medirlas.

10. Ejercicio práctico

Una empresa tiene tres tiendas y cinco productos. Los registros de inventario no se actualizan necesariamente todos los días. Se selecciona el intervalo del 1 al 30 de noviembre.

  1. ¿Por qué no debemos sumar las unidades de todos los días?
  2. ¿Qué ocurre si un producto no tiene registros en noviembre, pero sí en octubre?
  3. ¿Cómo se comporta la medida si el usuario filtra una sola tienda?
  4. ¿Qué diferencia existe entre recuperar la última observación y exigir un registro exactamente el 30 de noviembre?
  5. ¿Cómo identificarías los productos cuyo último inventario tiene más de siete días?

11. Solución del ejercicio

1. ¿Por qué no sumar todos los días? Porque el inventario es semi-aditivo en el tiempo. Sumar saldos diarios duplicaría las existencias a lo largo del periodo.

2. Si no hay registros en noviembre, pero sí en octubre: la medida de última observación recupera el último registro anterior o igual al 30 de noviembre, siempre que los filtros de producto y tienda permitan encontrarlo.

3. Si se filtra una sola tienda: el filtro de tienda permanece activo y la medida calcula las combinaciones visibles de esa tienda.

4. Última observación frente a fecha exacta: la primera recupera el último dato anterior o igual a la fecha de corte; la segunda solo devuelve el dato registrado exactamente ese día.

5. Detectar inventarios con más de siete días: compara la fecha de corte con la última fecha registrada:

Inventario Desactualizado :=
VAR Dias =
    [Dias desde Ultimo Inventario]
RETURN
    IF (
        NOT ISBLANK ( Dias ) && Dias > 7,
        1,
        0
    )

12. Conclusiones

El concepto más importante es que un total de inventario no debe calcularse sumando observaciones temporales. Primero hay que definir qué significa “inventario al cierre” para el negocio.

  • Usa la última observación por producto y tienda cuando necesites continuidad temporal.
  • Usa la fecha exacta cuando solo sean válidos los registros del día de cierre.
  • Itera por la granularidad adecuada para que los totales sean correctos.
  • Distingue siempre entre ausencia de datos, BLANK y cero.
  • Valida el coste de las iteraciones con DAX Studio antes de llevar el patrón a un modelo grande.

Este patrón también puede adaptarse a saldos bancarios, cartera de clientes, activos, deuda pendiente y otras magnitudes semi-aditivas. Dominarlo permite construir informes de Power BI mucho más fiables cuando el dato representa un saldo o estado a lo largo del tiempo.

¿De cuánta utilidad te ha parecido este contenido?

¡Haz clic en una estrella para puntuarlo!

Puntuación media 0 / 5. Recuento de votos: 0

Hasta ahora, ¡no hay votos!. Sé el primero en puntuar este contenido.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *