En el último artículo utilizamos DAX para analizar las ventas de una empresa a partir de una tabla en la que disponíamos de los precios para un determinado cliente, artículo y rango de fechas.

En este artículo vamos a complicar un poco más el requisito de negocio y veremos cómo podemos utilizar la función TREATAS para establecer relaciones virtuales en el caso de que existan registros de precios que no dependan de las columnas de relación, en el ejemplo del artículo anterior, el IdCliente y el IdArticulo, sino que utilicen otros atributos dimensionales para la asignación del importe.

En este ejemplo vamos a trabajar con precios de coste en lugar de con precios de venta. Supongamos que la tabla con la que tenemos que operar ahora es la siguiente:

Como vemos, disponemos de registros para distintos tipos de coste (arancel, embalaje…), cada uno de ellos asociado a un intervalo temporal. Algunos registros tienen un cliente asociado, pero otros se van a imputar en función de otros atributos dimensionales de producto o de cliente.

Supongamos que el requisito de negocio es el siguiente. Tenemos que calcular, con una medida, el coste del embalaje, el cual depende del segmento al cual pertenece el cliente seleccionado en el contexto de filtro. Para realizar este cálculo debemos tener en cuenta la unidad asociada al coste, ya que en el caso de que esta sea EUR/Kg o EUR_Kg, el importe de la tabla tenemos que multiplicarlo por el peso del artículo activo en el contexto. Por último, esta métrica solo debe devolvernos un valor en el caso de que otra medida, [LD Packaging], sea igual a cero o igual a BLANK.

Encontrando la solución

Empezamos examinando el modelo con el que trabajamos:

No hemos establecido ninguna relación entre la tabla de costes y la dimensión de producto, ya que el coste no está asociado a un artículo directamente. Tampoco tenemos una relación entre el coste y nuestra dimensión temporal porque, como en el artículo anterior, estamos trabajando con una tabla de intervalos de fechas, y los filtros para extraer los registros que se encuentran entre las fechas de inicio y fin debemos establecernos mediante código DAX.

Si que tenemos creada una relación con la dimensión cliente, pero el coste del embalaje no depende directamente del cliente sino de otro atributo dimensional, en este caso el segmento de negocio al que el cliente pertenece. Esta circunstancia hace que en el código de la medida tengamos que crear una relación virtual con la función TREATAS, pero no nos podemos olvidar que, con el objetivo de que esta relación funcione correctamente, tenemos que desactivar la relación física que existe a través de la clave primaria de la dimensión, el campo IdCliente. Para llevar a cabo esto último vamos a utilizar la función CROSSFILTER, que es la única función en DAX que nos permite desactivar una relación (sin tener que activar otra a la vez) durante el tiempo de procesamiento de una medida. Por tanto, la variable con la que obtendríamos el importe correcto de la tabla Costes NE sería:

VAR _Coste =
    CALCULATE (
        MAX ( 'Costes NE'[Coste] ),
        FILTER (
            ALL ( 'Costes NE'[Desde Fecha],
             'Costes NE'[Hasta Fecha] 
            ),
            'Costes NE'[Desde Fecha] <= _MaxFecha
                && 'Costes NE'[Hasta Fecha] >= _MaxFecha
        ),
        'Costes NE'[Tipo Coste] = "EMBALAJE",
        TREATAS ( 
            VALUES ( 'Customer'[Business Segment ID] ),
            'Costes NE'[Segmento] ),
        CROSSFILTER ( 'Customer'[IdCustomer], 
                      'Costes NE'[IdCustomer], NONE )
    )
DAX

Otro aspecto a considerar es la necesidad de obtener el valor de la columna Unidad, para decidir por cual variable tenemos que multiplicar el importe del coste obtenido. Para ello, tenemos que realizar una operación similar a la anterior, relacionar virtualmente por el segmento de negocio y desactivar la relación física establecida a través del IdCliente:

VAR _Unidad =
    CALCULATE (
        SELECTEDVALUE ( 'Costes NE'[Unidad] ),
        CROSSFILTER ( 'Customer'[IdCustomer], 
                      'Costes NE'[IdCustomer], NONE 
        ),
        TREATAS ( VALUES ( Customer[Business Segment ID] ),
                 'Costes NE'[Segmento] 
       )
    )
DAX

Con todo ello, e implementando un condicional para multiplicar por un valor u otro en función del campo Unidad, y otro para comprobar el valor de la medida [LD Packaging], el código completo que resuelve el requisito de negocio que establecimos al principio del artículo sería el siguiente:

Coste Embalaje =
VAR _MaxFecha =
    MAX ( Calendario[Fecha] )
VAR _Unidad =
    CALCULATE (
        SELECTEDVALUE ( 'Costes NE'[Unidad] ),
        CROSSFILTER ( 'Customer'[IdCustomer], 
                      'Costes NE'[IdCustomer], NONE 
        ),
        TREATAS (
            VALUES ( Customer[Business Segment ID] ),
            'Costes NE'[Segmento]
        )
    )
VAR _NetWeightItemSelected =
    SELECTEDVALUE ( 'Item'[Net Weight] )
VAR _Coste =
    CALCULATE (
        MAX ( 'Costes NE'[Coste] ),
        FILTER (
            ALL (
                'Costes NE'[Desde Fecha],
                'Costes NE'[Hasta Fecha]
            ),
            'Costes NE'[Desde Fecha] <= _MaxFecha
                && 'Costes NE'[Hasta Fecha] >= _MaxFecha
        ),
        'Costes NE'[Tipo Coste] = "EMBALAJE",
        TREATAS (
            VALUES ( 'Customer'[Business Segment ID] ),
            'Costes NE'[Segmento]
        ),
        CROSSFILTER ( 'Customer'[IdCustomer],
                      'Costes NE'[IdCustomer], NONE 
        )
    )
VAR _Rdo1 =
    _Coste
        * IF (
            OR (
                _Unidad = "Eur/Kg",
                _Unidad = "EUR_KG"
            ),
            _NetWeightItemSelected,
            IF (
                _Unidad = "Eur/Un",
                1
            )
        )
VAR _Rdo2 =
    IF (
        [LD Packaging] IN { 0, BLANK () },
        _Rdo1
    )
RETURN
    _Rdo2
DAX

 

Todo el código DAX de este artículo ha sido formateado con «DAX Formatter»
DAX Formatter by SQLBI