Informes y Documentos

RequestPage

Dificultad: 7/10

¿Qué pasa si tienes 1 millón de facturas y lanzas un Report sin filtros? El motor de BC bloqueará todo tratando de generar 45.000 páginas en PDF, destrozando la memoria del servidor. Para esto existe el RequestPage: la pantalla previa que aparece antes de que el informe empiece a procesar datos.

Estructura básica

La sección requestpage va dentro del Report, al mismo nivel que dataset:

AL
requestpage
{
    SaveValues = true;  // BC recuerda la última configuración del usuario

    layout
    {
        area(Content)
        {
            group(Opciones)
            {
                Caption = 'Opciones del Informe';

                field(IncluirDevueltos; IncluirDevueltos)
                {
                    ApplicationArea = All;
                    Caption         = 'Incluir préstamos ya devueltos';
                    ToolTip         = 'Marca esta casilla para ver también los préstamos devueltos.';
                }
            }
        }
    }

    trigger OnOpenPage()
    begin
        // Valores por defecto al abrir la RequestPage
        IncluirDevueltos := false;
    end;
}

¿Qué es la RequestPage?

Es una "mini ventana popup" que aparece siempre antes de que el motor empiece a recorrer los DataItems del informe. Esta ventana interrumpe al motor en seco y le da al usuario controles para acotar los datos: campos de fecha "Desde... Hasta...", selectores booleanos como Incluir Clientes Morosos, rangos de código, etc.

Los filtros estándar de los DataItems aparecen en la RequestPage de forma automática. Tú solo defines las opciones adicionales que quieras añadir.

SaveValues — Recordar la última configuración

SaveValues = true hace que BC recuerde la última configuración que usó el usuario. Muy cómodo para informes que se lanzan a diario con los mismos parámetros.

Añadir filtros de fecha

El caso más habitual: permitir que el usuario filtre el informe por un rango de fechas. Las variables definidas en la RequestPage son globales del Report, accesibles desde cualquier trigger y DataItem:

AL
requestpage
{
    SaveValues = true;

    layout
    {
        area(Content)
        {
            group(Opciones)
            {
                Caption = 'Opciones';
                field(IncluirDevueltos; IncluirDevueltos)
                {
                    ApplicationArea = All;
                    Caption         = 'Incluir préstamos devueltos';
                }
            }
            group(Fechas)
            {
                Caption = 'Rango de fechas';
                field(FechaDesde; FechaFiltroDesde)
                {
                    ApplicationArea = All;
                    Caption         = 'Fecha inicio: Desde';
                }
                field(FechaHasta; FechaFiltroHasta)
                {
                    ApplicationArea = All;
                    Caption         = 'Fecha inicio: Hasta';
                }
            }
        }
    }

    trigger OnOpenPage()
    begin
        // Valores por defecto: año en curso completo
        FechaFiltroDesde := DMY2Date(1, 1, Date2DMY(Today(), 3));  // 1 de enero del año actual
        FechaFiltroHasta := Today();
        IncluirDevueltos := false;
    end;
}

Usar las opciones en los DataItems

Las variables de la RequestPage se aplican en OnPreDataItem(), no en OnAfterGetRecord(). Así BC filtra antes de traer los datos, no después:

⚠️ Filtra siempre en OnPreDataItem

Si filtraras en OnAfterGetRecord, BC primero traería todos los registros y luego los descartaría uno a uno, lo cual es muy ineficiente. Filtra siempre en OnPreDataItem para que SQL solo devuelva lo que necesitas.

AL
dataitem(Prestamo; Prestamo)
{
    DataItemLink = "No. Socio" = FIELD("No.");

    trigger OnPreDataItem()
    begin
        // Aplicamos los filtros de fecha que el usuario eligió en la RequestPage
        if FechaFiltroDesde <> 0D then
            SetFilter("Fecha Inicio", '>=%1', FechaFiltroDesde);
        if FechaFiltroHasta <> 0D then
            SetFilter("Fecha Inicio", '<=%1', FechaFiltroHasta);

        // Si el usuario NO marcó "Incluir devueltos", los excluimos
        if not IncluirDevueltos then
            SetFilter(Estado, '<>%1', Estado::Devuelto);
    end;
}

Ejemplo completo: Report con RequestPage integrada

AL
report 50102 "Informe Completo Prestamos"
{
    Caption         = 'Informe Completo de Préstamos';
    UsageCategory   = ReportsAndAnalysis;
    ApplicationArea = All;

    dataset
    {
        dataitem(Socio; Socio)
        {
            column(Socio_No;     "No.")          { }
            column(Socio_Nombre; Nombre)         { }
            column(Socio_Total;  TotalDiasSocio) { }

            trigger OnPreDataItem()
            begin
                SetCurrentKey(Nombre);
            end;

            trigger OnAfterGetRecord()
            begin
                TotalDiasSocio := 0;
            end;

            dataitem(Prestamo; Prestamo)
            {
                DataItemLink = "No. Socio" = FIELD("No.");

                column(Prest_No;     "No.")           { }
                column(Prest_Inicio; "Fecha Inicio")  { }
                column(Prest_Dias;   "Dias Prestamo") { }
                column(Prest_Estado; Estado)          { }

                trigger OnPreDataItem()
                begin
                    // Filtros dinámicos desde la RequestPage
                    if FechaFiltroDesde <> 0D then
                        SetFilter("Fecha Inicio", '>=%1', FechaFiltroDesde);
                    if FechaFiltroHasta <> 0D then
                        SetFilter("Fecha Inicio", '<=%1', FechaFiltroHasta);
                    if not IncluirDevueltos then
                        SetFilter(Estado, '<>%1', Estado::Devuelto);

                    SetCurrentKey("Fecha Inicio");
                    SetAscending("Fecha Inicio", false);
                end;

                trigger OnAfterGetRecord()
                begin
                    TotalDiasSocio += "Dias Prestamo";
                end;
            }
        }
    }

    requestpage
    {
        SaveValues = true;
        layout
        {
            area(Content)
            {
                group(Opciones) { Caption = 'Opciones';
                    field(IncluirDevueltos; IncluirDevueltos) { ApplicationArea = All; Caption = 'Incluir devueltos'; }
                }
                group(Fechas) { Caption = 'Rango de fechas';
                    field(FechaDesde; FechaFiltroDesde) { ApplicationArea = All; Caption = 'Desde'; }
                    field(FechaHasta; FechaFiltroHasta) { ApplicationArea = All; Caption = 'Hasta'; }
                }
            }
        }
        trigger OnOpenPage()
        begin
            FechaFiltroDesde := DMY2Date(1, 1, Date2DMY(Today(), 3));
            FechaFiltroHasta := Today();
            IncluirDevueltos := false;
        end;
    }

    var
        TotalDiasSocio  : Integer;
        IncluirDevueltos: Boolean;
        FechaFiltroDesde: Date;
        FechaFiltroHasta: Date;

    rendering
    {
        layout(RDLCLayout)
        {
            Type       = RDLC;
            LayoutFile = 'src/layouts/InformeCompleto.rdlc';
        }
    }

    trigger OnPreReport()
    begin
        // Validamos que la fecha Desde no sea posterior a Hasta
        if (FechaFiltroDesde <> 0D) and (FechaFiltroHasta <> 0D) then
            if FechaFiltroDesde > FechaFiltroHasta then
                Error('La fecha "Desde" no puede ser posterior a la fecha "Hasta".');
    end;

    trigger OnPostReport()
    begin
        Message('Informe generado correctamente.');
    end;
}

💡 Tips

  • SaveValues = true guarda la última configuración del usuario. Ideal para informes que se ejecutan a diario con los mismos parámetros.
  • Las variables de la RequestPage son globales del Report: accesibles desde cualquier DataItem y trigger sin necesidad de pasarlas como parámetros.
  • Usa OnOpenPage() de la RequestPage para poner valores por defecto inteligentes (ej. año en curso, sin devueltos...).
  • Aplica los filtros siempre en OnPreDataItem, nunca en OnAfterGetRecord. El primero filtra en SQL antes de traer datos; el segundo los trae todos y los descarta uno a uno.
  • Valida las opciones del usuario en OnPreReport(): es el último punto antes de que el motor empiece a leer datos.
← Volver a Teoría