Gráficos en Business central

 


¿Cómo representar gráficamente datos en Business central?

Una de las posibles respuestas es utilizando Power Bi.
Pero Business central dispone de una forma de representar gráficamente datos como podemos ver en muchos rol center.


Hay que tener en cuenta que no es aconsejable acometer la representación de muchas medidas y periodos mediante este método sino representar medidas sencillas como por ejemplo, las ventas respecto a devoluciones de un producto por mes durante el último año o envíos contra devoluciones de un cliente o grupo de clientes durante el último año por meses.

Manejando los gráficos

Business central dispone del siguiente control que podremos utilizar para esta tarea:

Microsoft.Dynamics.Nav.Client.BusinessChart

Este control junto con la tabla Business Chart Buffer será todo lo que necesitemos para completar nuestro objetivo.
Vamos a ver cómo.

Nos basaremos en el ejemplo comentado anteriormente: ventas contra abonos de un cliente en un periodo de un año separado por meses.

Crearemos una extensión de la página 21 Customer Card

pageextension 50500 CustomerCardExt extends "Customer Card"
{
    layout
    {
        addfirst(content)
        {
            group(ShippingsVsReturns)
            {
                Caption = 'Envíos vs devoluciones';
                usercontrol(BusinessChart; "Microsoft.Dynamics.Nav.Client.BusinessChart")
                {
                    ApplicationArea = Invoicing, Basic, Suite;

                    trigger AddInReady()
                    begin
                        InitChart();
                    end;

                    trigger DataPointClicked(point: JsonObject)
                    begin
                        ChartPointClicked(point);
                    end;
                }
            }
        }
    }
}

Añadimos en el Layout, en la parte superior de la página nuestro control dentro de un grupo.
El control dispone de varios triggers, pero para el ejemplo usaremos estos 2:

  • AddInReady: en él cargaremos los datos a mostrar en el gráfico.
  • DataPointClicked: ocurre al pulsar en un elemento de gráfico. Para el ejemplo estaría bien que al pulsar en un elemento, envío o devolución nos mostrase qué movimientos componen ese valor.

Además cada vez que nos posicionemos en el registro actual, refrescaremos el gráfico por lo que volvemos a llamar a la función InitChart en cada obtención de registro.

    trigger OnAfterGetCurrRecord()
    begin
        InitChart();
    end;

Definimos las variables globales que utilizaremos para el ejemplo en la página.

var
        BusinessChartBuffer: Record "Business Chart Buffer" temporary;
        Last12Months: Dictionary of [Text, Dictionary of [Date, Date]];
        ActualMonth: Dictionary of [Date, Date];

Definimos la tabla BusinessChartBuffer que actuará en conjunción con el control para mostrar la gráfica.
He creado un diccionario que albergará los últimos 12 meses conteniendo en la primera parte, clave  o index un texto que en este caso será mes-año, por ejemplo: abril-2024. Y le acompaña un diccionario compuesto por fecha y fecha en el que almacenaremos las fechas de inicio y fin de cada mes para poder utilizar luego más cómodamente al crear los filtros necesarios.
Por último un diccionario de tipo fecha y fecha para poder manejar los valores de cada línea del diccionario Last12Months.
Ahora pasamos a las funciones restantes.

    #region InitChart
    local procedure InitChart()
    var
        MonthList: List of [Text];
        StartDate: Date;
        EndDate: Date;
        Month: Text;
        I: Integer;
    begin
        BusinessChartBuffer.Initialize();
        BusinessChartBuffer.AddMeasure('Envíos', 1, BusinessChartBuffer."Data Type"::Integer, BusinessChartBuffer."Chart Type"::Column.AsInteger()); //medida 1
        BusinessChartBuffer.AddMeasure('Devoluciones', 2, BusinessChartBuffer."Data Type"::Integer, BusinessChartBuffer."Chart Type"::Column.AsInteger()); //medida 2
        BusinessChartBuffer.SetXAxis('Mes', BusinessChartBuffer."Data Type"::String);

        Last12Months := GetLast12Months();
        MonthList := Last12Months.Keys;
        foreach Month in MonthList do begin
            BusinessChartBuffer.AddColumn(Month);
            Last12Months.Get(Month, ActualMonth);
            ActualMonth.Keys.Get(1, StartDate);
            ActualMonth.Values.Get(1, EndDate);
            BusinessChartBuffer.SetValueByIndex(0, I, GetMovs(Enum::"Item Ledger Document Type"::"Sales Shipment", StartDate, EndDate));
            BusinessChartBuffer.SetValueByIndex(1, I, GetMovs(Enum::"Item Ledger Document Type"::"Sales Return Receipt", StartDate, EndDate));
            I += 1;
        end;
        BusinessChartBuffer.Update(CurrPage.BusinessChart);
    end;

    local procedure GetLast12Months() Response: Dictionary of [Text, Dictionary of [Date, Date]]
    var
        ReferenceDate: Date;
        Dates: Dictionary of [Date, Date];
        MonthName: Text;
        I: Integer;
    begin
        ReferenceDate := CalcDate('<-1Y>', Today);
        ReferenceDate := CalcDate('<-CM>', ReferenceDate);
        for I := 1 to 12 do begin
            Clear(Dates);
            ReferenceDate := CalcDate('<1M>', ReferenceDate);
            Dates.Add(CalcDate('<-CM>', ReferenceDate), CalcDate('<CM>', ReferenceDate));
            MonthName := Format(ReferenceDate, 0, '<Month Text>') + '-' + Format(Date2DMY(ReferenceDate, 3));
            Response.Add(MonthName, Dates);
        end;
    end;

    local procedure GetMovs(MovType: Enum "Item Ledger Document Type"; StartDate: Date; EndDate: Date) Result: Integer
    var
        ItemLedgerEntry: Record "Item Ledger Entry";
        DocumentNo: Code[20];
    begin
        ItemLedgerEntry.Reset();
        ItemLedgerEntry.SetCurrentKey("Document No.");
        ItemLedgerEntry.SetRange("Source Type", ItemLedgerEntry."Source Type"::Customer);
        ItemLedgerEntry.SetRange("Document Type", MovType);
        ItemLedgerEntry.SetRange("Source No.", Rec."No.");
        ItemLedgerEntry.SetRange("Posting Date", StartDate, EndDate);
        ItemLedgerEntry.SetLoadFields("Document No.");
        if ItemLedgerEntry.FindSet() then
            repeat
                if DocumentNo <> ItemLedgerEntry."Document No." then begin
                    DocumentNo := ItemLedgerEntry."Document No.";
                    Result += 1;
                end;
            until ItemLedgerEntry.Next() = 0;
    end;
    #endregion InitChart

La función InitChart es la que rellena la gráfica con datos:

  • Inicializa la tabla BusinessChartBuffer y crea 2 medidas:
  1. Envíos
  2. Devoluciones

En cada mes aparecerán separados los envíos de las devoluciones

  • Obtenemos la definición de los últimos 12 meses: su nombre y las fechas de inicio y fin de estos. Para ello se llama a la función GetLast12Months.
  • Obtenemos las claves de los valores obtenidos en el punto anterior y recorremos cada uno de los 12 meses.
  • En ese recorrido creamos la columna mes en la tabla BusinessChartBuffer, que no es otro que el valor de la clave del diccionario Last12Months así como las fechas de inicio y fin de cada uno de los periodos.
  • Añadimos los valores nº de envíos y devoluciones a la tabla BusinessChartBuffer.
  • Y por último, actualizamos el control del gráfico con los valores indicados en la tabla.
Como anotaciones comentar que el tipo de gráfico elegido es el de barra porque me parece el más adecuado para este ejemplo, pero existen muchos otros, así como la posibilidad de combinar varios en el mismo control, por ejemplo columna 1 de tipo Column y columna 2 de tipo Line:


Seguimos...
La función GetLast12Months es la encargada de rellenar el diccionario con los valores de nombre del mes: mes-año y las fechas de inicio y fin de cada periodo.
Creamos una fecha de referencia que será hoy menos 1 año, pero situándonos en el primer día de ese mes y a partir de ahí vamos restando 1 mes a esa fecha 12 veces.
En cada resta de mes obtenemos el primer y último día del mes y rellenamos el diccionario Dates.
Luego obtenemos el nombre del periodo y añadimos este junto con el calendario conteniendo fecha inicio y fin al diccionario principal.

La función GetMovs recorre los movimientos de producto del cliente y cuenta distintos documentos diferenciando envíos de devoluciones.

Por último creamos la función encargada de ejecutar la acción correspondiente cuando el usuario pulse una barra con un valor en la gráfica.

    #region UserClickOnChart
    local procedure ChartPointClicked(point: JsonObject)
    var
        SalesShipmentHeader: Record "Sales Shipment Header";
        ReturnReceiptHeader: Record "Return Receipt Header";
        JArray: JsonArray;
        JToken: JsonToken;
        Type: Option Shippings,Returns;
        StartDate: Date;
        EndDate: Date;
    begin
        if point.Get('Measures', JToken) then begin
            JArray := JToken.AsArray();
            JArray.Get(0, JToken);
            if JToken.AsValue().AsText() = 'Envíos' then
                Type := Type::Shippings
            else
                Type := type::Returns;
            if point.Get('XValueString', JToken) then begin
                Last12Months.Get(JToken.AsValue().AsText(), ActualMonth);
                ActualMonth.Keys.Get(1, StartDate);
                ActualMonth.Values.Get(1, EndDate);
                if Type = Type::Shippings then begin
                    SalesShipmentHeader.SetRange("Sell-to Customer No.", Rec."No.");
                    SalesShipmentHeader.SetRange("Posting Date", StartDate, EndDate);
                    Page.Run(Page::"Posted Sales Shipments", SalesShipmentHeader);
                end else begin
                    ReturnReceiptHeader.SetRange("Sell-to Customer No.", Rec."No.");
                    ReturnReceiptHeader.SetRange("Posting Date", StartDate, EndDate);
                    Page.Run(Page::"Posted Return Receipts", ReturnReceiptHeader);
                end;
            end;
        end;
    end;
    #endregion UserClickOnChart

La pulsación de una de las barras o valores que componen la gráfica devuelve un JSON Object que descomponemos para obtener los valores que necesitamos para continuar.
Obtenemos la medida (Measures) del valor pulsado. Tal y como hemos definido el ejemplo, o es Envío o devolución.
En la variable XValueString tendremos el texto o nombre de la columna pulsada, por ejemplo abril-2024 que "casualmente" coincide con la clave de nuestro diccionario Last12Months por lo que localizamos ese valor y rescatamos las fechas de inicio y fin.
Ahora que ya conocemos los datos necesarios, sólo resta filtrar los datos a mostrar y mostrar la página correspondiente: envíos o devoluciones.

Veamos el control en funcionamiento:



Como vemos, podemos crear un gráfico de barras mostrando la información que necesitemos con muy poco esfuerzo.
Especial atención a la tabla BusinessChartBuffer y el control que muestra la gráfica ya que es la calve del post.
Este ejemplo sería fácilmente reutilizable para otros casos de uso.

Y hasta aquí este post. Espero que os sirva de ayuda.

Como siempre, el código en github.


Publicar un comentario

Añade comentario (0)

Artículo Anterior Artículo Siguiente