Saltar al contenido principal

18 · Agrupar filas y emitir subtotales

Escenario

Tu libro de facturas / hojas de liquidación / órdenes de compra tiene filas de líneas divididas en secciones por cliente o por mes, con una línea de subtotal tras cada sección y (opcionalmente) una línea de total general al final:

Acme Producto A 10.000
Acme Producto B 5.000
Subtotal 15.000
Beta Producto A 20.000
Subtotal 20.000
Total general 35.000

XTL 0.6 trae dos directivas que hacen esto dentro de un único bloque de datos, sin pre-agregar en el origen ni post-procesar la salida (ADR-0038).

Las dos piezas

@group [Key1], [Key2], …

@group particiona el conjunto de filas activo en N niveles anidados para emitir subtotales intercalados. NO reordena las filas — el orden de grupo es el orden de aparición después de que @filter y @sort se hayan aplicado. Combínalo con @sort sobre las mismas claves para conseguir un orden de grupo estable.

{{ @sort [Cliente] }}
{{ @group [Cliente] }}

@subtotal <agregado>

Una fila que contiene una celda {{ @subtotal SUM([Importe]) }} es una fila de subtotal. No itera por fila de origen; en su lugar, el renderizador la emite una vez en cada frontera de grupo al nivel asociado a la fila. Los agregados admitidos son SUM, COUNT, AVERAGE, MIN, MAX.

La primera fila @subtotal en orden de origen se vincula a la clave de grupo más interna. Apila filas @subtotal adicionales debajo para vincular a niveles más externos — la fila más baja se vincula a la clave más externa.

Agrupación de un solo nivel

{{ @sort [Cliente] }}
{{ @group [Cliente] }}
{{ [Cliente] }} | {{ [Producto] }} | {{ [Importe] }}
"Subtotal" | | {{ @subtotal SUM([Importe]) }}

Para tres filas de origen (Acme/Producto/100, Beta/Tornillo/50, Acme/Engranaje/200) esto renderiza:

Acme Producto 100
Acme Engranaje 200
Subtotal 300
Beta Tornillo 50
Subtotal 50

Anidado de dos niveles + total general

{{ @sort [Region] }}
{{ @sort [Cliente] }}
{{ @group [Region], [Cliente] }}
{{ [Region] }} | {{ [Cliente] }} | {{ [Importe] }}
"Subtotal cliente" | | {{ @subtotal SUM([Importe]) }}
"Subtotal región" | | {{ @subtotal SUM([Importe]) }}

La fila @subtotal superior (Subtotal cliente) se vincula a la clave más interna ([Cliente]); la siguiente fila (Subtotal región) se vincula a [Region]. Ambas emiten en las fronteras; la interna se dispara antes que la externa cuando ambas terminan simultáneamente.

El patrón "total general vía el subtotal más externo": con un único @group [Cliente] más dos filas @subtotal, la externa se dispara exactamente una vez — al final del bloque de datos — porque la frontera del grupo externo ES el final de los datos.

Composición con otras directivas

DirectivaInteracción
@filterLos filtros aplican antes de agrupar. Las filas filtradas no están en ningún grupo. Un grupo cuyas filas se filtraron por completo simplemente no aparece.
@sortLos ordenamientos aplican antes de agrupar. Para fijar el orden de grupo, @sort por las mismas claves que @group en el mismo orden.
@sourceCada bloque @source tiene su propio ámbito de agrupación.
@joinLas columnas de filas unidas participan en la agrupación como columnas de filas primarias. Las claves de grupo PUEDEN referenciar columnas unidas.
@topAplica después de agrupar al nivel de fila. Los subtotales solo se emiten para grupos cuyas filas de datos sobrevivieron al corte de @top.
@repeat rightIncompatible con @group (xl3/directive/invalid-syntax).

Casos límite

  • Caso degenerado de un solo grupo — si @group [Key] y todas las filas comparten un valor de [Key], el subtotal sigue emitiendo una vez en la frontera de ese grupo. Coincide con el patrón de total general cuando el conjunto de datos contiene un solo valor de grupo externo.
  • Grupos vacíos — un grupo cuyas filas de datos están todas vacías (según ADR-0007) se omite: ni filas de datos ni @subtotal emiten.
  • Argumentos de agregado — dentro de @subtotal solo se aceptan referencias de columna. Las expresiones compuestas (SUM([A]) - SUM([B]), IF(...)) lanzan xl3/subtotal/bad-aggregate y están aplazadas.
  • Celdas con texto literal en una fila @subtotal — perfectamente válido; la etiqueta "Subtotal:" se sitúa junto a la celda del agregado, y ambas se renderizan en cada emisión. Las celdas literales NO DEBEN referenciar columnas de la fila actual; no hay fila actual en una frontera de grupo.

Errores

  • xl3/group/missing-key — directiva @group sin lista de claves.
  • xl3/subtotal/outside-group — celda @subtotal en un bloque sin @group, o más filas @subtotal que claves de @group.
  • xl3/subtotal/bad-aggregate — el cuerpo no es uno de SUM, COUNT, AVERAGE, MIN, MAX, o su argumento no es una referencia de columna.

Véase también