Dif. 8/10

Informes Avanzados

Esta sección amplía lo que ya sabes sobre la arquitectura básica de los informes en BC. Cubre los cuatro diseñadores disponibles y cuándo elegir cada uno, cómo estructurar informes de resumen, las propiedades específicas del layout Word, el flujo para modificar un informe existente y los informes de solo procesamiento que ejecutan lógica AL sin generar ningún documento visual.

1. Los Cuatro Diseñadores de Informes de BC

Business Central soporta cuatro tipos de layout para sus informes. Cada uno responde a una necesidad distinta y se gestiona con una herramienta diferente. Un mismo informe puede tener varios layouts simultáneos y el usuario elige cuál usar en el momento de ejecutarlo.

Tipo de Layout Herramienta de diseño Extensión Cuándo usarlo
RDLC SQL Server Report Builder / Visual Studio .rdlc Informes con layout preciso, maquetación compleja, subreports y gráficos incrustados
Word Microsoft Word .docx Documentos con texto enriquecido, cartas, facturas con formato corporativo
Excel Microsoft Excel .xlsx Exportación de datos para análisis, tablas dinámicas, usuarios de negocio
External Herramienta externa (ej. Crystal Reports, SSRS) Variable Integraciones con sistemas de reporting externos ya existentes

RDLC — El más potente para maquetación

RDLC (Report Definition Language Client-side) es el formato nativo de BC para informes con maquetación precisa. Permite controlar píxel a píxel la posición de cada elemento, incrustar gráficos, usar expresiones condicionales de formato y crear subreports anidados. Su contrapartida es que requiere SQL Server Report Builder o Visual Studio para editarlo, lo cual es menos accesible para usuarios no técnicos.

Word — El más accesible para documentos

El layout Word usa marcadores (bookmarks) dentro de un documento .docx para indicar dónde BC debe insertar cada campo de datos. Es ideal para facturas, albaranes y cualquier documento con texto libre y formato corporativo, ya que cualquier usuario con Word puede editarlo sin conocimientos técnicos.

Excel — Para análisis de datos

El layout Excel genera un fichero .xlsx donde BC vuelca los datos del informe en formato tabular. El diseñador puede preconfigurar hojas, tablas dinámicas y gráficos en el template, y BC los rellena con los datos reales al ejecutar el informe.

💡 ¿Cuál elegir?

  • Para documentos de negocio (facturas, pedidos, albaranes): Word.
  • Para listados y análisis que el usuario exportará a Excel: Excel layout.
  • Para informes internos complejos con gráficos y agrupaciones visuales precisas: RDLC.
  • En un informe sin layout definido, BC genera automáticamente un layout RDLC básico.

2. Tipos de Informes de Resumen

Un informe de resumen (summary report) es aquel que agrupa y totali­za los datos en lugar de listarlos registro a registro. En lugar de mostrar cada préstamo individual, muestra el total de préstamos por socio, por mes o por categoría de libro. Son los más usados en reporting de negocio.

Informe de detalle vs. informe de resumen

Característica Detalle Resumen
Nivel de datos Un registro por línea Un grupo por línea (varios registros agregados)
Volumen de salida Alto (tantas filas como registros) Reducido (una fila por grupo)
Uso típico Listados de movimientos, auditoría Totales por cliente, ventas por mes, ranking
Mecanismo AL DataItem sin agrupación DataItem con GroupBy en Query, o acumuladores en triggers

Técnica 1: Acumuladores en triggers del DataItem

La forma más directa de crear un resumen en AL es usar variables acumuladoras en los triggers OnAfterGetRecord y OnPostDataItem:

report 50600 "Resumen Prestamos por Socio"
{
    UsageCategory = ReportsAndAnalysis;
    ApplicationArea = All;
    DefaultRenderingLayout = LayoutWord;

    dataset
    {
        dataitem(Socio; Socio)
        {
            // Columnas del nivel padre (socio)
            column(NoSocio;    Socio."No.")    { }
            column(NombreSocio; Socio.Nombre) { }
            column(TotalPrestamos; TotalPrest) { } // acumulador

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

                trigger OnAfterGetRecord()
                begin
                    TotalPrest += 1; // contamos cada préstamo
                end;
            }

            trigger OnPreDataItem()
            begin
                TotalPrest := 0; // reiniciamos el contador por cada socio
            end;
        }
    }

    var
        TotalPrest: Integer;

    rendering
    {
        layout(LayoutWord)
        {
            Type    = Word;
            Caption = 'Layout Word';
            LayoutFile = 'ResumenPrestamos.docx';
        }
    }
}

Técnica 2: Usar una Query como DataItem

La alternativa más limpia para informes de resumen es basar el informe en una Query con agrupación. La Query realiza el GROUP BY directamente en SQL, lo que es mucho más eficiente que acumular en AL:

// Query que agrupa préstamos por socio
query 50601 "Prestamos por Socio"
{
    QueryType = Normal;

    elements
    {
        dataitem(Prestamo; Prestamo)
        {
            column(NoSocio; "No. Socio") { }
            column(TotalPrestamos; "No.")
            {
                Method = Count; // GROUP BY implícito
            }
        }
    }
}

// Report usando la Query como fuente de datos
report 50602 "Resumen Desde Query"
{
    UsageCategory = ReportsAndAnalysis;
    ApplicationArea = All;

    dataset
    {
        dataitem(ResumenQ; "Prestamos por Socio")
        {
            column(NoSocio;        ResumenQ.NoSocio)        { }
            column(TotalPrestamos; ResumenQ.TotalPrestamos) { }
        }
    }
}

💡 Acumuladores vs. Query

  • Usa acumuladores en triggers cuando necesites lógica de negocio compleja que no cabe en una query (condiciones, conversiones, cálculos sobre múltiples tablas).
  • Usa una Query como DataItem cuando el resumen sea puramente agregaciones de datos. Es más rápido y el código es más limpio.

3. Microsoft Word — Propiedades del Informe

Un informe con layout Word usa un fichero .docx como plantilla. BC inyecta los datos en esa plantilla mediante marcadores (bookmarks) de Word que corresponden exactamente con los nombres de las columnas definidas en el dataset del informe.

Propiedades clave en AL para Word layout

Propiedad Dónde se pone Qué hace
DefaultRenderingLayout En el report Indica qué layout se usa por defecto si el usuario no elige
Type = Word Dentro del bloque layout Declara que este layout es de tipo Word
LayoutFile Dentro del bloque layout Nombre del fichero .docx incluido en la extensión
Caption Dentro del bloque layout Nombre que verá el usuario al seleccionar el layout
WordMergeDataItem En el report Indica cuál es el DataItem raíz para la combinación de correspondencia

Cómo funciona el sistema de marcadores

En el documento Word, cada dato del informe se representa con un marcador cuyo nombre coincide exactamente (respetando mayúsculas) con el nombre de la columna en el dataset. BC busca esos marcadores al renderizar y sustituye su contenido por el valor real del campo.

// En AL: definición del informe con Word layout
report 50610 "Ficha Socio Word"
{
    UsageCategory          = ReportsAndAnalysis;
    ApplicationArea        = All;
    DefaultRenderingLayout = LayoutWord;
    WordMergeDataItem      = Socio; // DataItem raíz para Word

    dataset
    {
        dataitem(Socio; Socio)
        {
            // Los nombres de columna deben coincidir con los bookmarks en el .docx
            column(NoSocio;     Socio."No.")         { }
            column(NombreSocio; Socio.Nombre)       { }
            column(EmailSocio;  Socio.Email)         { }
            column(FechaHoy;    Today)               { }
        }
    }

    rendering
    {
        layout(LayoutWord)
        {
            Type       = Word;
            Caption    = 'Ficha de Socio';
            LayoutFile = 'FichaSocio.docx'; // fichero en la carpeta del proyecto
        }
    }
}

// En el fichero FichaSocio.docx deberás tener marcadores con estos nombres:
// «NoSocio»    → se reemplaza por el valor del campo "No."
// «NombreSocio» → se reemplaza por Nombre
// «EmailSocio»  → se reemplaza por Email
// «FechaHoy»    → se reemplaza por la fecha actual

Cómo crear los marcadores en Word

⚠️ Errores comunes con Word layouts

  • Si el nombre del marcador no coincide exactamente con la columna del dataset, BC simplemente lo deja vacío sin dar error.
  • No uses espacios ni caracteres especiales (tildes, ñ) en los nombres de columna del dataset si van a usarse como marcadores Word.
  • El fichero .docx debe estar en la misma carpeta que el fichero .al del informe dentro del proyecto.

4. Modificar un Informe con el Diseñador o Word

Una vez que un informe está publicado en BC, puedes modificar su layout directamente desde la interfaz sin tocar el código AL. BC permite exportar el layout actual, editarlo con la herramienta correspondiente y volver a importarlo.

Flujo para modificar un layout Word

Paso Dónde Acción
1 BC → Gestión de layouts de informe Busca el informe por nombre o número y selecciona el layout Word
2 BC → Acción "Exportar layout" Descarga el fichero .docx actual en tu equipo
3 Microsoft Word Modifica el diseño, añade el logo, cambia fuentes, reordena los marcadores
4 BC → Acción "Importar layout" Sube el fichero modificado. BC lo valida y lo guarda
5 BC → Ejecutar el informe Verifica el resultado con datos reales

Flujo para modificar un layout RDLC

Paso Dónde Acción
1 BC → Gestión de layouts Selecciona el layout RDLC y usa "Exportar layout"
2 SQL Server Report Builder Abre el fichero .rdlc, modifica tablix, expresiones o gráficos
3 BC → Acción "Importar layout" Sube el RDLC modificado

Crear un layout personalizado desde VS Code

Si el informe no tiene layout o quieres añadir uno nuevo desde cero directamente en el proyecto de extensión, el flujo en VS Code es:

// 1. En el fichero .al del informe, genera el dataset primero y despliega la extensión
// 2. En BC, abre el informe y usa "Gestión de layouts"
// 3. Acción "Crear layout" → elige Word o RDLC
// 4. BC genera una plantilla base con todos los campos del dataset
// 5. Exporta esa plantilla, edítala y vuelve a importarla

// Alternativa desde VS Code: usar el comando AL:
// Ctrl+Shift+P → "AL: Generate Report Layout"
// Esto genera automáticamente el fichero .docx o .rdlc con los campos del dataset
// y lo añade al proyecto como fichero linked en el bloque rendering

rendering
{
    layout(LayoutWord)
    {
        Type       = Word;
        Caption    = 'Layout Principal';
        LayoutFile = 'MiInforme.docx'; // generado con AL: Generate Report Layout
    }
}

💡 Layouts en producción vs. en desarrollo

  • Los layouts que importas desde la UI de BC son layouts de usuario: se guardan en la base de datos y tienen prioridad sobre los layouts incluidos en la extensión.
  • Los layouts del fichero .al son los de la extensión: son el punto de partida pero pueden ser sobreescritos por los de usuario.
  • Para devolver un informe a su layout original de extensión, borra el layout de usuario desde "Gestión de layouts".

5. Informes de Solo Procesamiento

Un informe de solo procesamiento (processing-only report) es un informe que no genera ningún documento visible. Su función es ejecutar lógica AL de forma masiva sobre un conjunto de registros: actualizar datos, hacer cálculos, enviar emails, llamar a servicios externos… usando la misma infraestructura de RequestPage y DataItem que un informe normal, pero sin producir ninguna salida visual.

¿Por qué usar un informe en lugar de un codeunit?

Cómo declararlo en AL

Basta con añadir la propiedad ProcessingOnly = true en el informe. Con esto BC sabe que no debe buscar ni renderizar ningún layout.

report 50620 "Actualizar Estado Prestamos"
{
    UsageCategory  = Tasks;
    ApplicationArea = All;
    ProcessingOnly = true; // sin layout, sin salida visual
    Caption        = 'Actualizar Estado de Préstamos Vencidos';

    dataset
    {
        dataitem(Prestamo; Prestamo)
        {
            // El DataItem recorre los préstamos activos
            DataItemTableView = where(Estado = const(Activo));

            trigger OnAfterGetRecord()
            begin
                // Si la fecha límite ha pasado, marcamos como vencido
                if Prestamo."Fecha Devolucion" < Today then begin
                    Prestamo.Estado := Prestamo.Estado::Vencido;
                    Prestamo.Modify(true);
                    ContadorActualizados += 1;
                end;
            end;

            trigger OnPostDataItem()
            begin
                Message('Proceso completado. Préstamos actualizados: %1', ContadorActualizados);
            end;
        }
    }

    // RequestPage: el usuario puede filtrar por fechas antes de ejecutar
    requestpage
    {
        layout
        {
            area(Content)
            {
                group(Opciones)
                {
                    Caption = 'Opciones';
                    field(FechaCorte; FechaCorte)
                    {
                        Caption         = 'Fecha de Corte';
                        ApplicationArea = All;
                        ToolTip         = 'Préstamos anteriores a esta fecha se marcarán como vencidos.';
                    }
                }
            }
        }
    }

    var
        ContadorActualizados: Integer;
        FechaCorte:           Date;

    trigger OnPreReport()
    begin
        // Si no se especificó fecha de corte, usamos hoy
        if FechaCorte = 0D then
            FechaCorte := Today;

        ContadorActualizados := 0;
    end;
}

Cuándo usar ProcessingOnly vs. Codeunit

Situación Mejor opción Motivo
El usuario necesita filtrar qué registros procesar ProcessingOnly Report La RequestPage da esa interfaz automáticamente
El proceso se lanza desde un botón en una página Codeunit Más directo, sin pantalla de configuración extra
Se programa para ejecutarse automáticamente por la noche ProcessingOnly Report o Codeunit Ambos son compatibles con el Job Queue de BC
Proceso sobre miles de registros con filtros complejos ProcessingOnly Report El DataItem aplica filtros SQL de forma optimizada

💡 Buenas prácticas

  • Siempre muestra un mensaje al final del proceso (OnPostDataItem) indicando cuántos registros se procesaron. El usuario necesita saber que algo ocurrió.
  • Usa Modify(true) en lugar de Modify(false) para que se disparen los triggers de la tabla y se mantenga la integridad de los datos.
  • Si el proceso puede ser largo, considera usar Dialog para mostrar una barra de progreso durante la ejecución.
  • Un informe ProcessingOnly no necesita el bloque rendering. Si lo añades BC lo ignorará.
← Volver a Teoría