Listados de impresión
Plantillas y ejemplos
Esqueletos listos para copiar al diseñador de listados y rellenar. El porqué de cada pieza está en el artículo de listados de impresión.
Consulta primaria de un listado de documento#
La estructura mínima: el ID del documento como parámetro runtime, [CondicionesListado] al final del WHERE y un único ORDER BY por la clave del documento. El ejemplo es para factura de venta — para otro documento, cambia las tablas y el parámetro de ID.
SELECT
Cabecera.SERIE,
Cabecera.NUMDOC,
Cabecera.FECHA,
Lineas.CODART,
Lineas.DESCLIN,
Lineas.UNIDADES,
Lineas.PRCMONEDA,
Lineas.BRUTOMONEDA
-- añade aquí tus columnas; toda columna calculada, con CAST a tipo de longitud explícita
FROM CABEFACV Cabecera WITH (NOLOCK)
INNER JOIN LINEFACT Lineas WITH (NOLOCK) ON Lineas.IDFACV = Cabecera.IDFACV
WHERE Cabecera.IDFACV = :IdFacV
AND [CondicionesListado,Sysname,1=1]
ORDER BY Cabecera.IDFACV
Texto según el idioma del documento#
CASE Cabecera.CODIDIOMA
WHEN 'CAT' THEN 'Text en català'
WHEN 'CAS' THEN 'Texto en castellano'
ELSE 'Text in English'
END
Textos legales por empresa emisora e idioma#
Cuando los textos son largos (cláusulas RGPD, datos registrales) y hay varias empresas emisoras en el mismo listado — un grupo que factura con varias sociedades —, los CASE se quedan cortos. El patrón es cargar una tabla variable indexada por NIF de la empresa emisora + idioma y cruzarla con la cabecera. Cada empresa lleva sus variantes de idioma, con NULL como fila de respaldo (normalmente, el inglés):
DECLARE @Temp_lopd TABLE (
CodIdioma VARCHAR(20),
NifEmpresa VARCHAR(20),
NombreYDireccion VARCHAR(500),
RegistroMercantil VARCHAR(500),
TextoLopd VARCHAR(2500));
INSERT INTO @Temp_lopd (NifEmpresa, CodIdioma, NombreYDireccion, RegistroMercantil, TextoLopd) VALUES ('B00000000', 'CAT',
'EMPRESA EXEMPLE, S.L.' + CHAR(13) + CHAR(10) + 'N.I.F. B00000000' + CHAR(13) + CHAR(10) + 'Adreça - CP Ciutat',
'Inscrita en el R.M. de ..., vol. ..., foli ..., full ..., inscripció ...',
'Text legal RGPD en català.');
INSERT INTO @Temp_lopd (NifEmpresa, CodIdioma, NombreYDireccion, RegistroMercantil, TextoLopd) VALUES ('B00000000', 'CAS',
'EMPRESA EJEMPLO, S.L.' + CHAR(13) + CHAR(10) + 'N.I.F. B00000000' + CHAR(13) + CHAR(10) + 'Dirección - CP Ciudad',
'Inscrita en el R.M. de ..., tomo ..., folio ..., hoja ..., inscripción ...',
'Texto legal RGPD en castellano.');
INSERT INTO @Temp_lopd (NifEmpresa, CodIdioma, NombreYDireccion, RegistroMercantil, TextoLopd) VALUES ('B00000000', NULL,
'EMPRESA EJEMPLO, S.L.' + CHAR(13) + CHAR(10) + 'N.I.F. B00000000' + CHAR(13) + CHAR(10) + 'Address - ZIP City',
'Registered in ...',
'RGPD legal text in English.');
-- se repiten los tres INSERT por cada empresa emisora del grupo
SELECT
...,
Lopd.NombreYDireccion,
Lopd.RegistroMercantil,
Lopd.TextoLopd
FROM CABEFACV Cabecera WITH (NOLOCK)
...
LEFT JOIN @Temp_lopd Lopd
ON Cabecera.CODIDIOMA = Lopd.CodIdioma
AND DatosEmp.NIFEMP = Lopd.NifEmpresa
Si el grupo incorpora una empresa nueva, hay que darla de alta con sus tres variantes — si falta alguna, los documentos de esa empresa saldrán sin texto legal en ese idioma.
Concatenar filas en una cadena (seguro para el motor de informes)#
Concatena valores relacionados en una sola columna. Las dos precauciones que ya trae puestas son las que evitan los errores del motor de informes — sin ORDER BY en la subconsulta anidada, y CAST final a una longitud explícita en lugar de dejar el VARCHAR(MAX) que produce FOR XML PATH.
CAST(STUFF((
SELECT ', ' + Hijas.DESCLIN
FROM LINEFACT Hijas WITH (NOLOCK)
WHERE Hijas.IDFACV = Cabecera.IDFACV
FOR XML PATH('')
), 1, 2, '') AS VARCHAR(2000))
Subconsulta de rectificativas compatible con el SII#
La función interna de a3ERP DetalleRectificativa('V') identifica las facturas rectificadas como TipoContable/Serie/NumDoc; para el SII, en cambio, el identificador de una factura es Serie/NumDoc. Si al cliente le aplica el SII, esta subconsulta sustituye a la función y monta el texto con el formato correcto. Cubre los dos tipos de rectificativa — por facturas concretas y por periodo — y solo devuelve fila cuando la factura es rectificativa:
-- Solo devuelve fila cuando la factura es rectificativa (RECTIFICATIVA = 'T').
SELECT
Cabecera.IdFacV,
CAST(Cabecera.MotivoRectificacion AS VARCHAR(500)) AS MotivoRectificacion,
-- CAST final a VARCHAR acotado obligatorio. La concatenacion y el .value
-- producen VARCHAR(MAX), tipo que el dataset de a3ERP no sabe mapear.
CAST(CASE
WHEN Cabecera.TIPORECTIFICATIVA = 'PERIODO' THEN
CASE Cabecera.CodIdioma
WHEN 'CAT' THEN 'Rectificació del període '
WHEN 'CAS' THEN 'Rectificación del periodo '
ELSE 'Rectification of period '
END
+ CONVERT(VARCHAR(10), Cabecera.FECHAINIRECTIFICATIVA, 103)
+ ' - '
+ CONVERT(VARCHAR(10), Cabecera.FECHAFINRECTIFICATIVA, 103)
ELSE
CASE Cabecera.CodIdioma
WHEN 'CAT' THEN 'Rectificació de les factures: '
WHEN 'CAS' THEN 'Rectificación de las facturas: '
ELSE 'Rectification of invoices: '
END
+ ISNULL(
STUFF((
SELECT ', '
+ LTRIM(ISNULL(Origen.Serie, ''))
+ '/'
+ CAST(CAST(Origen.NumDoc AS BIGINT) AS VARCHAR(30))
FROM CABEFACV Origen WITH (NOLOCK)
WHERE Origen.IdFacV IN (
SELECT Detalle.IDFACORIGEN
FROM __DETALLERECTIFICADAS_VENTAS Detalle WITH (NOLOCK)
WHERE Detalle.IDFACRECTIFICATIVA = Cabecera.IdFacV
)
-- Sin ordenar dentro del FOR XML; el orden queda el natural por IdFacV.
FOR XML PATH(''), TYPE
).value('.', 'VARCHAR(MAX)'), 1, 2, ''),
'')
END AS VARCHAR(2000)) AS TextoRectificacion
FROM CABEFACV Cabecera WITH (NOLOCK)
WHERE Cabecera.IdFacV = :IdFacV
AND Cabecera.RECTIFICATIVA = 'T'
ORDER BY Cabecera.IdFacV
Esta plantilla es además un buen repaso de las reglas del artículo, todas aplicadas a la vez: parámetro runtime :IdFacV, CAST a longitud explícita en cada columna calculada, concatenación sin ORDER BY anidado, ORDER BY final por la clave del documento y comentarios sin dos puntos. Y un matiz que se ve en los literales ('Rectificación de las facturas: '): los dos puntos dentro de cadenas de texto no dan problema — la restricción es en los comentarios.
Al adaptar las plantillas
Nada de : ni [...] en los comentarios, y cuidado con escribir las palabras ORDER BY en uno — las tres trampas están explicadas en el artículo.