domingo, 16 de diciembre de 2018

► Cálculo de facturas vencidas

Es muy común tener que hacer listados en las que se muestren facturas vencidas a partir de unos rangos de vencimiento. Por ejemplo de 0-1 meses, 1 a 2 meses, 2 a 3 meses, 3 a 6 meses.... etc.

Y he visto todo tipo de soluciones para resolver el problema, desde crear expresiones con complejos Set Analysis hasta tablas de conversión de periodos de vencimiento pasando por complejos if anidados tanto en script como en expresiones.

Esta solución es una más y la que yo suelo usar.

Consiste en añadir un campo más con el periodo de vencimiento a cada registro de factura. Este periodo se calcula en el script y se actualiza cada vez que se recarga la aplicación, lo que mejora luego bastante su rendimiento porque no hará falta ningún set analysis ni ifs anidados en las expresiones.

Para no complicar mucho el tema, imaginemos que tenemos esta tabla de facturas:

FACTURAS:
LOAD * INLINE [
Num_factura, FechaFactura, FechaVencimiento
1 ,          13/02/2018,   13/03/2018
2,           13/02/2018,   25/08/2018
3,           25/07/2018,   25/11/2018
]

Y las queremos clasificar en los siguientes rangos de vencimiento de modo que el usuario seleccione uno de ellos y automáticamente nos aparezca un listado con las facturas vencidas de ese periodo.

0 a 1 meses, 1 a 2 meses, 2 a 3 meses, 3 a 6 meses, 6 a 12 meses. 12 a 24 meses, 24 a 36 meses y más de 36 meses.

Siguiendo la regla de oro de que todo lo que se pueda calcular en el script hay que hacerlo en el script mejor que en expresiones de objetos, calculamos el rango de vencimiento y lo añadimos como un nuevo campo.

LEFT JOIN(FACTURAS)
LOAD *,
Pick(ALT(
       
IF(Interval(today()-FechaVencimiento,'D') <30, 1),
       
IF(Interval(today()-FechaVencimiento,'D') <60, 2),
       
IF(Interval(today()-FechaVencimiento,'D') <90, 3),
       
IF(Interval(today()-FechaVencimiento,'D') <180, 4),
       
IF(Interval(today()-FechaVencimiento,'D') <365, 5),
       
IF(Interval(today()-FechaVencimiento,'D') <730, 6),
       
IF(Interval(today()-FechaVencimiento,'D') <1095, 7),
        8),
   '0 a 1', '1 a 2', '2 a 3', '3 a 6', '8 a 12', '12 a 24', '24 a 36',' más 36 meses') &' meses' 
AS RangoDeuda
RESIDENT   FACTURAS; 

Como vemos, el LEFT JOIN se hace leyendo la propia tabla  (LEF JOIN Y RESIDENT tienen como origen y destino la misma tabla), sin necesidad de usar una tabla auxiliar intermedia.

Utilizo ALT() por evitar los IF anidados, que siempre son más engorrosos, pero como ALT() sólo devuelve valores numéricos y no puede devolver directamente el nombre del rango, hay que recoger su valor en un PICK() .

El resultado final del ejemplo sería algo así:

Cuando el usuario elige un rango, la tabla se filtra automáticamente.


martes, 4 de diciembre de 2018

► GetObjectDimension y GetObjectExpression





Existen dos funciones en Qlik Sense que estuvieron documentadas en las primeras versiones y, aunque siguen funcionando (verificado hasta la versión de Noviembre de 2018), han desaparecido de la documentación sin dejar rastro.

Son GetObjectDimension(índice) y GetObjectExpresion(índice)

GetObjectDimension(0) Devuelve la etiqueta de la dimensión actualmente seleccionada en un grupo de dimensiones alternativas.

GetObjectExpresion(0) Devuelve la etiqueta de la expresión actualmente seleccionada en un grupo de expresiones alternativas.

Ojo, lo que devuelve es la etiqueta y quizá este sea el motivo de que hayan desaparecido de la documentación. Aunque siguen funcionando, es posible que discontinúen su funcionamiento. Quizá de problemas cuando la etiqueta de la expresión/dimensión es una variable.

Veamos un ejemplo.

Vamos a suponer que queremos un gráfico que acumule las ventas durante un periodo de tiempo. En Sense no existe el Tic de QlikView para acumular, por tanto lo tenemos que hacer con una fórmula.
Para agravar la situación, el gráfico tiene 3 dimensiones alternativas. Mes, Semana  y Día. Para permitir mostrar la información, necesitamos 3 expresiones distintas, una que acumule por Mes, otra por Semana y otra por Día y que cada una se ejecute dependiendo de la dimensión seleccionada.

El resultado debería ser este:




La configuración de datos y expresiones queda como en la siguiente imagen. Observad que hay 3 dimensiones alternativas y sólo una medida.



La expresión de la medida es esta:


ALT(
   
IF(GetObjectDimension(0)='Mes Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Month]))),([Month],(NUMERIC, ASCENDING))))),
   
IF(GetObjectDimension(0)='Dia del Año Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Dia del Año]))),([Dia del Año],(NUMERIC, ASCENDING))))),
    IF(
GetObjectDimension(0)='Semana Venta',
             
SUM(aggr(rangesum(above(SUM({<[Year]={'$(vL.AñoActual)'}>} [Precio Neto]+[Iva Compra]),0,num([Week]))),([Week],(NUMERIC, ASCENDING)))))
)


Vamos a diseccionarla:

  • ·         En lugar de utilizar IF anidados, usamos la función ALT, que es mucho más legible. Cada IF devuelve un valor numérico (si se cumple) o nulo, en cuyo caso el flujo del programa pasa al siguiente IF.
  • ·         El valor con el que se compara GetObjectDimension(0) en cada IF es el texto que hayamos puesto en la etiqueta de la dimensión (ver imagen superior).
  • ·         $(vL.AñoActual) Es una variable que contiene el año máximo de los seleccionados (=Max(Year)).
  • ·         SUM(aggr(rangesum(above(SUMEs la expresión que calcula toda la suma de ventas acumulada para cada unidad de tiempo (Month, Dia del Año o Week).
  • ·         (NUMERIC, ASCENDING) Son dos parámetros (exclusivos de Sense y de View a partir de la versión 12) de la función AGGR que permiten ordenarla. Sin estos parámetros AGGR se ordena según el orden de carga.