FilterGroups en Business Central: Conjuntos de filtros

0
28

Los FilterGroups representan uno de los mecanismos de filtrado más sofisticados y potentes en Microsoft Dynamics Business Central y Dynamics NAV, proporcionando un sistema multicapa que permite escenarios de filtrado complejos organizando los filtros en grupos separados y numerados que se combinan durante la ejecución de consultas.

Qué son los FilterGroups y su propósito en ambas plataformas

FilterGroups son contenedores lógicos que almacenan conjuntos de filtros aplicados a objetos Record o RecordRef. Cada FilterGroup se identifica mediante un número entero y contiene filtros establecidos previamente usando las funciones SETFILTER o SETRANGE.

Tipos de FilterGroups y casos de uso específicos

NúmeroNombrePropósito técnico
-1Cross-columnLógica OR: Los registros coinciden si se cumple CUALQUIER condición
0StandardGrupo predeterminado: Filtros modificables por el usuario
1GlobalAplicación amplia: Filtros aplicados a toda la sesión de aplicación
2FormFiltros en página: SetTableView, SourceTableView.
3ExecTiempo de ejecución: SubPageView, RunPageView.
4LinkRelacional:DataItemLink,SubPageLink
5TempActualmente en desuso
6SecurityPermisos: Filtrado de datos basado en permisos de usuario
7FactboxesEstado UI: Gestiona estado de visualización y limpieza de factbox

Ejemplos de uso

    procedure EjemploBasicoFilterGroup()
    var
        Customer: Record Customer;
        Text000: Label 'El filtergroup original es: %1';
        Text001: Label 'El filtergroup actual es: %1';
    begin
        Rec.SetFilter("No.", '10000..30000');
        Message(Text000, Rec.FilterGroup);
        Rec.FilterGroup(100);
        Rec.setrange("Location Code", 'GRIS');
        Message(Text001, Rec.FilterGroup);
        Rec.FilterGroup(0);
        Message(Text001, Rec.FilterGroup);
    end;

En este ejemplo se comprueba que por defecto se utiliza el grupo 0, luego se puede cambiar al 100 y luego otra vez al 0.
Además en los filtros del usuario se ve que únicamente puede modificar los que están en el grupo 0 pero están los del grupo 100 y no pueden eliminarse (Usualmente lo vemos en la aplicación usando el 2 y no el 100 pero puede usarse cualquier numero que no sea el 0 y es preferible evitar los que utiliza el sistema salvo necesidad de concretamente ello):

procedure CrossColumnSearchExample()
    var
        Customer: Record Customer;
        SearchString: Text;
    begin
        Customer.FilterGroup(-1);
        SearchString := '@*GAR*';
        Customer.SetFilter(Name, SearchString);
        Customer.SetFilter(contact, SearchString);
        // Para visualización UI, debe usar marcado
        if Customer.FindSet() then
            repeat
                Customer.Mark(true);
            until Customer.Next() = 0;
        Customer.FilterGroup(0);
        Customer.MarkedOnly(true);
        Page.Run(Page::"Customer List", Customer);
    end;

Filtrará para que si tiene GAR en el nombre o en el contacto salga

    procedure GetFiltersTest()
    var
        Customer: Record Customer;
        OriginalGroup: Integer;
        SavedFilters: Text;
    begin
        message(format(Rec.GetFilters()));  //Nombre: *s, Filtro fecha: ''..01/01/28
        Rec.FilterGroup(10);
        Rec.SetFilter("No.", '10000..20000');
        message(format(Rec.GetFilters()));  //Nº: 10000..20000
        Rec.FilterGroup(0);
        message(format(Rec.GetFilters())); //Nombre: *s, Filtro fecha: ''..01/01/28
    end;

Cuando utilizamos FilterGroups tenemos en contenedores distintos los filtros de forma que para consultar los filtros utilizados deberemos darnos un paseo por todos los FilterGroups activos. De esta forma en el ejemplo anterior cuando estamos en el FG10 solo vemos los filtros aplicados en el 10.
Aunque no los veamos todos están activos:

Es importante este hecho dado que si queremos saber los filtros tendremos que conocer que FilterGroups hay que:

    procedure GetActiveFilterGroups()
    var
        ActiveGroups: List of [Integer];
        i: Integer;
        Filters: Text;
    begin
        Rec.FilterGroup(10);
        Rec.SetFilter("No.", '10000..20000');
        Rec.FilterGroup(8);
        Rec.SetFilter("No.", '30000');

        //Crear lista con los FilterGroups activos
        for i := 0 to 255 do begin
            Rec.FilterGroup(i);
            Filters := Rec.GetFilters();
            if Filters <> '' then
                ActiveGroups.Add(i);
        end;

        message(format(ActiveGroups.Count()));  //3

        //Recorrer lista con FilterGroups activos
        foreach i in ActiveGroups do begin
            Rec.FilterGroup(i);
            Filters := Rec.GetFilters();
            Message('FilterGroup %1: %2', i, Filters);  
            //FilterGroup 0: Nombre: *s*, Filtro fecha: ''..01/01/28
            //FilterGroup 8: Nº: 30000
            //FilterGroup 10: Nº: 10000..20000
        end;
        Rec.FilterGroup(0);
    end;

Otro método de filtrar la información, Mark

Si bien esto no es un FilterGroup, tiene relación con ellos por cuanto no muestran los filtros al usuario y no le dejan modificarlos.
La ventaja y desventaja de este método, con respecto al filtrado con Setrange o Setfilter, es la selección 1 a 1 de los registros a mostrar lo que hace poder establecer criterios complejos de selección, por ejemplo, filtrar los registros en los que la suma del campo importe 1 e importe 2 sea superior a 100.
Una vez establecidos para mostrar al usuario el usuario no tiene modo de ver otros registros que los seleccionados aunque dentro de ellos si podrá utilizar los filtros de usuario.

trigger OnDrillDown()
    var
        PurchaseHeader: Record "Purchase Header";
        ComprasACR: Codeunit "Compras ACR";
        PurchaseQuotes: Page "Purchase Quotes AMV";
    begin
        PurchaseHeader.RESET;
        PurchaseHeader.FILTERGROUP(73);
        PurchaseHeader.SETRANGE("Document Type", PurchaseHeader."Document Type"::Quote);
        PurchaseHeader.SETRANGE("Condicion 1", FALSE);
        PurchaseHeader.SETRANGE("Condicion 2", FALSE);
        IF PurchaseHeader.FINDSET THEN
            REPEAT
                IF CumpleCondicion3(PurchaseHeader) THEN BEGIN
                    IF PurchaseHeader."Condicion 4" THEN
                        PurchaseHeader.MARK(TRUE);
                END ELSE
                    PurchaseHeader.MARK(TRUE);
            UNTIL PurchaseHeader.NEXT = 0;

        //Quito los filtros porque no quiero que el usuario piense que es lo único que se aplica
        PurchaseHeader.SETRANGE("Condicion 1");
        PurchaseHeader.SETRANGE("Condicion 2");

        PurchaseHeader.FILTERGROUP(0);
        PurchaseHeader.MARKEDONLY(TRUE);
        PurchaseQuotes.SETTABLEVIEW(PurchaseHeader);
        PurchaseQuotes.RUN;
    end;

Errores a evitar

  • Evitar utilizar FilterGroups reservados. Casi en todos los sitios he encontrado el FilterGroup 2 para establecer filtros que el usuario no pueda modificar pero es un error utilizar un FilterGroup que utiliza la aplicación. Tienes hasta el 255, hay donde elegir.
  • Cuantos mas FilterGroups se utilizan mas sentencias Where en SQL y menor eficiencia.
  • Volver siempre al FilterGroup 0: tras aplicar filtros en otros grupos, asegúrate de devolver el contexto al grupo 0. Evitar olvidarlo reduce comportamientos inesperados en siguientes operaciones.
  • Orden de aplicación de filtros: recuerda que BC combina filtros de todos los grupos activos mediante AND, salvo el -1 (Cross-column) que usa OR. Esto puede dar resultados sorprendentes si no se tienen en cuenta.
  • Diferencia entre MARK y FILTERGROUP en rendimiento: MARK genera un conjunto marcado en memoria, mientras que los filtros se traducen en cláusulas SQL. En tablas grandes, los filtros suelen ser mucho más eficientes.

Conclusiones

  • Los FilterGroups son una herramienta poderosa en Business Central para gestionar filtrados avanzados, combinando lógica y control sobre qué puede modificar el usuario.
  • Es fundamental usar grupos personalizados y documentados, evitando los reservados por el sistema, para mantener claridad y evitar conflictos.
  • Aunque permiten gran flexibilidad, un uso excesivo puede afectar al rendimiento SQL, por lo que conviene usarlos con moderación y priorizar SETRANGE o SETFILTER sobre MARK cuando sea posible.
  • Conocer bien cómo funcionan los FilterGroups (especialmente los especiales como -1 y 6) permite evitar confusiones en depuración, seguridad y desarrollo de extensiones.