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 totaliza 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
- En Word: Insertar → Marcador. Escribe el nombre exacto del campo (sin espacios ni caracteres especiales) y haz clic en Agregar.
- Para tablas repetidas (una fila por registro del DataItem), BC usa una tabla de Word donde la primera fila de datos contiene los marcadores. Al renderizar, BC duplica esa fila por cada registro.
- Los marcadores de tabla siguen el patrón
NombreDataItem_NombreColumna.
⚠️ 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
.docxdebe estar en la misma carpeta que el fichero.aldel 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
.alson 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?
- El informe hereda automáticamente la RequestPage para que el usuario introduzca filtros antes de ejecutarlo.
- El DataItem recorre los registros de forma optimizada, aplicando los filtros del usuario automáticamente.
- Aparece en el menú de BC como cualquier otro informe y puede programarse con el programador de trabajos.
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 deModify(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
Dialogpara mostrar una barra de progreso durante la ejecución. - Un informe
ProcessingOnlyno necesita el bloquerendering. Si lo añades BC lo ignorará.