Botones en Business central

Para la app móvil puede surgir la necesidad de crear una página personalizada en la que dispongamos de una serie de botones enlazados con acciones de Business central:

  • Mostrar la lista de productos
  • Ver una pantalla de configuración
  • Mostrar otra página personalizada, por ejemplo un calendario con la planificación de fabricación, esto lo veremos en un post más adelante
  • etc.

 

En Business central estamos limitados a las acciones de la barra inferior, pilas o elementos con un link, que no es poco ... pero ¿y si dispusiéramos de la opción de añadir botones a voluntad?

Creando pantalla con botones

Para este post voy a utilizar algo que seguro que nos suena o nos es familiar: Bootstrap
Bootstrap ya dispone de lo necesario para no tener que preocuparnos de si un elemento web, un botón en nuestro caso, se ajusta bien a la página junto con otros, o si se recorta, etc. en resumen, si disponemos de una fila de 5 botones alineados de izquierda a derecha y cambiamos el tamaño de la pantalla y esa disposición no se puede mostrar sin cortarse, automáticamente los botones que no quepan se mostrarán en otra línea. Lo que se conoce como responsivo.

A mayores, nuestros botones dispondrán de un icono y cómo tampoco nos vamos a volver locos diseñando un icono, luego otro y otro que mantengan el mismo "tema", tamaños, etc. utilizaremos unas fuentes llamadas fontawesome.

Si queréis ampliar información, aquí dejo link a wikipedia de Bootstrap

Mirando en el último link, vamos a localizar algunos iconos en fontawesome para nuestra pantalla, por ejemplo:
  • una lista de productos, encontramos el icono list y pulsando sobre él aparece el código html a utilizar

    copiamos el código y lo reservamos para luego <i class="fa-solid fa-list"></i>

  • localizamos el icono calendar-days y repetimos la operación

        <i class="fa-regular fa-calendar-days"></i>

  • print: <i class="fa-solid fa-print"></i>
  • truck: <i class="fa-solid fa-truck"></i>
  • industry: <i class="fa-solid fa-industry"></i>
  • ...
 
El resultado que buscamos, con los iconos elegidos es algo similar a esto:
y trasladando el resultado a cómo lo veríamos en un móvil, por ejemplo:


Como vemos, los botones se muestran todos seguidos en la misma fila, pero al cambiar de vista a una más pequeña (la del móvil) se ha ajustado sólo a 2 líneas de botones sin necesidad de controlar esto mediante código.

Vamos a ver cómo hacer todo esto, explicaremos algunos conceptos y de paso cambiaremos algunos colores.

Primero crearemos un carpeta llamada Addin en la que crearemos un fichero llamado buttons.al con este contenido:

controladdin buttons
{
    MinimumHeight = 600;
    MinimumWidth = 200;
    RequestedHeight = 600;
    RequestedWidth = 200;
    HorizontalShrink = true;
    HorizontalStretch = true;
    VerticalShrink = true;
    VerticalStretch = true;

    StartupScript = './Addin/startup.js';
    Scripts = 'https://code.jquery.com/jquery-3.7.1.min.js', 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/js/bootstrap.min.js';
    StyleSheets = './Addin/buttons.css', 'https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css';

    event buttonclick(data: Text);
    event OnStartup();

    procedure loaddata(data: Text);
}

como vemos, el addin arranca un un fichero javascript startup.js y utilizamos una página de estilos local: buttons.css, el resto de elementos necesarios se descarga de cdn, en el código pongo enlaces a la última versión a fecha de este post.

Ahora creamos un fichero con el estilo para nuestros botones llamado buttons.css
.btn-sq-lg {
    width: 90px !important;
    height: 120px !important;    
    margin-right: 10px;      
    margin-top: 5px;
    font-size: small;   
  }

En él estamos indicando altura, anchura y separación entre los botones además del tamaño de la fuente del texto, se puede ajustar a las necesidades sin problema ya que es muy sencillo de entender.

Ahora crearemos el fichero javascript que controla los botones: los muestra y nos informa del click en ellos, startup.js

Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('OnStartup')

window.loaddata = function (data) {    
    var iframe = document.getElementById('controlAddIn');    
    iframe.innerHTML = data;        
   
    var bootstraps = iframe.querySelectorAll('a');
   
    bootstraps.forEach(function(bootstrap) {                      
        bootstrap.addEventListener('click', function() {
            var bootId = this.id;
            console.log('Pulsado ' + bootId);
            Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('buttonclick',[bootId]);                        
        });
    });
}

Aquí observamos como cargamos el código recibido en la variable data, que pasaremos desde Business central y luego localizamos todos los botones y le añadimos una acción en el evento click, en este caso que nos muestre en consola el nº del botón pulsado y que devuelva a Business central en nº de botón pulsado. Así sabremos qué botón se ha pulsado y podremos ejecutar desde Business central la acción pertinente.

Por último la página que mostraremos en la aplicación móvil, creamos una nueva página de tipo card
page 50400 "Button Test"
{
    ApplicationArea = All;
    Caption = 'Prueba botones';
    PageType = Card;
    UsageCategory = Tasks;


    layout
    {
        area(content)
        {
            usercontrol(botonera; buttons)
            {
                trigger OnStartup()
                begin
                    SetBootstrapButtonData();
                end;

                trigger buttonclick(data: Text)
                begin
                    case data of
                        '1':
                            SetBootstrapButtonData();
                        '2':
                            SetBootstrapButtonData2();
                        '3':
                            SetBootstrapButtonData3();
                        else
                            Message('Pulsado botón ' + data);
                    end;
                end;
            }
        }
    }

    var
        InitDiv: Label '<div class="container"><div class="row"><div class="col-lg-12"><p>';
        ButtonSource: Label '<a href="#" id="%1" class="btn btn-sq-lg btn-primary"><i class="fas %2 fa-2x"></i><br/><br/>%3<br>%4</a>';
        ButtonSource2: Label '<a href="#" id="%1" class="btn btn-sq-lg btn-success"><i class="fas %2 fa-2x"></i><br/><br/>%3<br>%4</a>';
        ButtonSource3: Label '<a href="#" id="%1" class="btn btn-sq-lg btn-info"><i class="fas %2 fa-2x"></i><br/><br/>%3<br>%4</a>';
        EndDiv: Label '</p></div></div>';

    local procedure SetBootstrapButtonData()
    var
        TextBuilder: TextBuilder;
    begin
        TextBuilder.Append(InitDiv);
        TextBuilder.Append(StrSubstNo(ButtonSource, '1', 'fa-list', 'Lista', 'productos'));
        TextBuilder.Append(StrSubstNo(ButtonSource, '2', 'fa-calendar-days', 'Planif.', 'producción'));
        TextBuilder.Append(StrSubstNo(ButtonSource, '3', 'fa-print', 'Imprimir', 'etiquetas'));
        TextBuilder.Append(StrSubstNo(ButtonSource, '4', 'fa-truck', 'Ver carga', 'camión'));
        TextBuilder.Append(StrSubstNo(ButtonSource, '5', 'fa-industry', 'Comenzar', 'picking'));
        TextBuilder.Append(EndDiv);

        CurrPage.botonera.loaddata(TextBuilder.ToText());
    end;

    local procedure SetBootstrapButtonData2()
    var
        TextBuilder: TextBuilder;
    begin
        TextBuilder.Append(InitDiv);
        TextBuilder.Append(StrSubstNo(ButtonSource2, '1', 'fa-list', 'Lista', 'productos'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '2', 'fa-calendar-days', 'Planif.', 'producción'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '3', 'fa-print', 'Imprimir', 'etiquetas'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '4', 'fa-truck', 'Ver carga', 'camión'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '5', 'fa-industry', 'Comenzar', 'picking'));
        TextBuilder.Append(EndDiv);

        CurrPage.botonera.loaddata(TextBuilder.ToText());
    end;

    local procedure SetBootstrapButtonData3()
    var
        TextBuilder: TextBuilder;
    begin
        TextBuilder.Append(InitDiv);
        TextBuilder.Append(StrSubstNo(ButtonSource3, '1', 'fa-list', 'Lista', 'productos'));
        TextBuilder.Append(StrSubstNo(ButtonSource3, '2', 'fa-calendar-days', 'Planif.', 'producción'));
        TextBuilder.Append(StrSubstNo(ButtonSource3, '3', 'fa-print', 'Imprimir', 'etiquetas'));
        TextBuilder.Append(StrSubstNo(ButtonSource3, '4', 'fa-truck', 'Ver carga', 'camión'));
        TextBuilder.Append(StrSubstNo(ButtonSource3, '5', 'fa-industry', 'Comenzar', 'picking'));
        TextBuilder.Append(EndDiv);

        CurrPage.botonera.loaddata(TextBuilder.ToText());
    end;
}

Al iniciar el control cargamos los botones, están numerados del 1 al 5, con el nombre de los iconos que hemos copiado anteriormente de fontawesome y utilizando una plantilla de línea rellena en el label ButtonSource
Al pulsar en el botón 1: Lista productos carga los botones con esa plantilla, al pulsar el botón 2: Planif. producción carga los mismos botones en color verde, el botón 3 produce el mismo resultado en un tono de azul más claro y el resto muestra un mensaje con el nº de botón pulsado

Respecto a los colores de los botones, en este link, podemos ver las distintas opciones.
Propongo 3 opciones como ejemplo para poder ver las distintas posibilidades, pero vemos que hay varias más.

Ahora restaría añadir los botones que necesitemos, si queremos separarlos por líneas, es decir, bloques de 3 por ejemplo, o de 2, ... deberíamos encapsular las líneas de esos 3 botones en un div, por ejemplo en el código tenemos 5 botones, vamos a poner 3 y 2 al pulsar el botón 2 y llamar a la función SetBootstrapButtonData2
    local procedure SetBootstrapButtonData2()
    var
        TextBuilder: TextBuilder;
    begin
        TextBuilder.Append(InitDiv);
        TextBuilder.Append('<div>');
        TextBuilder.Append(StrSubstNo(ButtonSource2, '1', 'fa-list', 'Lista', 'productos'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '2', 'fa-calendar-days', 'Planif.', 'producción'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '3', 'fa-print', 'Imprimir', 'etiquetas'));
        TextBuilder.Append('</div><div>');
        TextBuilder.Append(StrSubstNo(ButtonSource2, '4', 'fa-truck', 'Ver carga', 'camión'));
        TextBuilder.Append(StrSubstNo(ButtonSource2, '5', 'fa-industry', 'Comenzar', 'picking'));
        TextBuilder.Append('</div>');
        TextBuilder.Append(EndDiv);

        CurrPage.botonera.loaddata(TextBuilder.ToText());
    end;

Vemos cómo los botones 1 al 3 están en un bloque div y los otros 2 botones en otro bloque div.
El resultado es el siguiente:

Carga normal o pulsando el botón 1 (lista productos)
Carga pulsando el botón 2 (planif. producción)

Y por último nos faltaría añadir las acciones correspondientes a cada uno de los botones una vez los hayamos puesto en nuestra pantalla.

Espero que os sirve de ayuda.

Como siempre el código en github


Publicar un comentario

Añade comentario (0)

Artículo Anterior Artículo Siguiente