Home > JasperReports > Reporte maestro-detalle usando JRXMLDatasource de JasperReports 3.1.2

Reporte maestro-detalle usando JRXMLDatasource de JasperReports 3.1.2

March 26th, 2009

Introducción

Este es un ejemplo de como realizar un reporte maestro detalle usando JasperReports.
Vamos a generar un simple pdf con el detalle de una factura(Invoice).
También pueden ver Como usar el JRXmlDataSource de JasperReports 3.1.2, para una introducción al JRXmlDataSource.

Archivo XML de datos

bueno, esta es la parte mas simple. :)

(invoice.xml)

<?xml version="1.0" encoding="UTF-8"?>
 
<invoice>
    <invoiceNumber>BP-0123</invoiceNumber>
    <invoiceDate>03/01/2009</invoiceDate>
    <clientName>ACME INC</clientName>
    <clientId>BP102USA</clientId>
    <products>
        <product>
            <name>Iphone 6000G</name>
            <quantity>12</quantity>
            <amout>4000</amout>
        </product>
        <product>
            <name>Lenovo T61P</name>
            <quantity>5</quantity>
            <amout>7000</amout>
        </product>
        <product>
            <name>Xbox 123</name>
            <quantity>2</quantity>
            <amout>1000</amout>
        </product>
        <product>
            <name>GOLDPATH 1</name>
            <quantity>4</quantity>
            <amout>400</amout>
        </product>
    </products>
</invoice>

Como verán, solo definimos datos como, nombre del cliente y el detalle de los productos.

Creación de diseños invoice.jrxml y invoice-detail.jrxml

Tanto los diseños del reporte maestro y detalle son los mismos. Como el de detalle es el mas simple lo listare primero y luego el maestro.

(invoice-detail.jrxml)

<?xml version="1.0" encoding="UTF-8"  ?>
<!-- Created with iReport - A designer for JasperReports -->
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="invoice-detail">
	<property name="ireport.scriptlethandling" value="0" />
	<property name="ireport.encoding" value="UTF-8" />
 
	<field name="name" class="java.lang.String">
		<fieldDescription><![CDATA[name]]></fieldDescription>
	</field>
	<field name="quantity" class="java.lang.String">
		<fieldDescription><![CDATA[quantity]]></fieldDescription>
	</field>
	<field name="amout" class="java.lang.Integer">
		<fieldDescription><![CDATA[amout]]></fieldDescription>
	</field>
 
	<variable name="totalAmount" class="java.lang.Integer" resetType="Report" calculation="Sum">
		<variableExpression><![CDATA[$F{amout}]]></variableExpression>
	</variable>
		<background>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</background>
		<title>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</title>
		<pageHeader>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</pageHeader>
		<columnHeader>
			<band height="32"  isSplitAllowed="true" >
				<staticText>
					<reportElement
						x="15"
						y="8"
						width="192"
						height="18"
						key="staticText-1"/>
					<box></box>
					<textElement>
						<font isBold="true"/>
					</textElement>
				<text><![CDATA[Product Name]]></text>
				</staticText>
				<staticText>
					<reportElement
						x="228"
						y="8"
						width="131"
						height="18"
						key="staticText-2"/>
					<box></box>
					<textElement>
						<font isBold="true"/>
					</textElement>
				<text><![CDATA[Quantity]]></text>
				</staticText>
				<staticText>
					<reportElement
						x="381"
						y="8"
						width="108"
						height="18"
						key="staticText-3"/>
					<box></box>
					<textElement textAlignment="Right">
						<font isBold="true"/>
					</textElement>
				<text><![CDATA[Amout]]></text>
				</staticText>
			</band>
		</columnHeader>
		<detail>
			<band height="38"  isSplitAllowed="true" >
				<textField isStretchWithOverflow="false" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="15"
						y="11"
						width="192"
						height="18"
						key="textField"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
				</textField>
				<textField isStretchWithOverflow="false" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="228"
						y="11"
						width="131"
						height="18"
						key="textField"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.String"><![CDATA[$F{quantity}]]></textFieldExpression>
				</textField>
				<textField isStretchWithOverflow="false" pattern="¤ #,##0.00" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="381"
						y="11"
						width="108"
						height="18"
						key="textField"/>
					<box></box>
					<textElement textAlignment="Right">
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.Integer"><![CDATA[$F{amout}]]></textFieldExpression>
				</textField>
				<staticText>
					<reportElement
						x="492"
						y="14"
						width="31"
						height="14"
						key="staticText-4"/>
					<box></box>
					<textElement>
						<font size="8"/>
					</textElement>
				<text><![CDATA[USD]]></text>
				</staticText>
			</band>
		</detail>
		<columnFooter>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</columnFooter>
		<pageFooter>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</pageFooter>
		<summary>
			<band height="33"  isSplitAllowed="true" >
				<textField isStretchWithOverflow="false" pattern="¤ #,##0.00" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="364"
						y="7"
						width="125"
						height="26"
						key="textField"/>
					<box></box>
					<textElement textAlignment="Right">
						<font size="14"/>
					</textElement>
				<textFieldExpression   class="java.lang.Integer"><![CDATA[$V{totalAmount}]]></textFieldExpression>
				</textField>
				<staticText>
					<reportElement
						x="492"
						y="9"
						width="26"
						height="14"
						key="staticText-5"/>
					<box></box>
					<textElement>
						<font size="8"/>
					</textElement>
				<text><![CDATA[USD]]></text>
				</staticText>
				<staticText>
					<reportElement
						x="15"
						y="7"
						width="112"
						height="26"
						key="staticText-6"/>
					<box></box>
					<textElement>
						<font size="14" isBold="true"/>
					</textElement>
				<text><![CDATA[TOTAL]]></text>
				</staticText>
			</band>
		</summary>
</jasperReport>

Como veran, se definen tres campos para los productos: name, quantity y amount, luego estos campos son desplegados en el área de detalle del reporte.

Ademas tambien estamos declarando una variable llamada totalAmount, que nos sirve calcular el total de la factura.

(invoice.jrxml)

<?xml version="1.0" encoding="UTF-8"  ?>
<!-- Created with iReport - A designer for JasperReports -->
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="invoice">
	<property name="ireport.scriptlethandling" value="0" />
	<property name="ireport.encoding" value="UTF-8" />
 
	<parameter name="detailPath" isForPrompting="false" class="java.lang.String"/>
 
	<field name="clientName" class="java.lang.String">
		<fieldDescription><![CDATA[clientName]]></fieldDescription>
	</field>
	<field name="clientId" class="java.lang.String">
		<fieldDescription><![CDATA[clientId]]></fieldDescription>
	</field>
	<field name="invoiceNumber" class="java.lang.String">
		<fieldDescription><![CDATA[invoiceNumber]]></fieldDescription>
	</field>
	<field name="invoiceDate" class="java.lang.String">
		<fieldDescription><![CDATA[invoiceDate]]></fieldDescription>
	</field>
 
		<background>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</background>
		<title>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</title>
		<pageHeader>
			<band height="50"  isSplitAllowed="true" >
				<staticText>
					<reportElement
						x="5"
						y="6"
						width="227"
						height="35"
						key="staticText-2"/>
					<box></box>
					<textElement>
						<font fontName="FreeSerif" size="24"/>
					</textElement>
				<text><![CDATA[SomeCompany co.]]></text>
				</staticText>
			</band>
		</pageHeader>
		<columnHeader>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</columnHeader>
		<detail>
			<band height="136"  isSplitAllowed="true" >
				<staticText>
					<reportElement
						x="460"
						y="43"
						width="0"
						height="0"
						key="staticText-1"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<text><![CDATA[Static Text]]></text>
				</staticText>
				<textField isStretchWithOverflow="false" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="95"
						y="30"
						width="173"
						height="18"
						key="textField"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.String"><![CDATA[$F{clientName}+ "  - " + $F{clientId}]]></textFieldExpression>
				</textField>
				<textField isStretchWithOverflow="false" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="94"
						y="7"
						width="100"
						height="18"
						key="textField"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.String"><![CDATA[$F{invoiceNumber}]]></textFieldExpression>
				</textField>
				<staticText>
					<reportElement
						x="4"
						y="30"
						width="26"
						height="18"
						key="staticText-3"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<text><![CDATA[To:]]></text>
				</staticText>
				<staticText>
					<reportElement
						x="4"
						y="7"
						width="81"
						height="18"
						key="staticText-4"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<text><![CDATA[Invoice Number:]]></text>
				</staticText>
				<staticText>
					<reportElement
						x="5"
						y="53"
						width="82"
						height="18"
						key="staticText-5"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<text><![CDATA[Date:]]></text>
				</staticText>
				<textField isStretchWithOverflow="false" isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None"  hyperlinkTarget="Self" >
					<reportElement
						x="96"
						y="53"
						width="78"
						height="18"
						key="textField"/>
					<box></box>
					<textElement>
						<font/>
					</textElement>
				<textFieldExpression   class="java.lang.String"><![CDATA[$F{invoiceDate}]]></textFieldExpression>
				</textField>
				<subreport  isUsingCache="true">
					<reportElement
						x="8"
						y="90"
						width="521"
						height="29"
						key="subreport-1"/>
					<dataSourceExpression><![CDATA[((net.sf.jasperreports.engine.data.JRXmlDataSource)$P{REPORT_DATA_SOURCE}).subDataSource($P{detailPath})]]></dataSourceExpression>
                    <subreportExpression  class="java.lang.String"><![CDATA["./invoice-detail.jasper"]]></subreportExpression>
				</subreport>
			</band>
		</detail>
		<columnFooter>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</columnFooter>
		<pageFooter>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</pageFooter>
		<summary>
			<band height="0"  isSplitAllowed="true" >
			</band>
		</summary>
</jasperReport>

Acá es donde se pone un poco confuso :) Donde tenemos que poner nuestra atención es en el elemento subreport. Este elemento también puede ser creado usando el iReport.

reportElement, define la posición donde se encontrara el subreporte.

dataSourceExpression, en la expresion estamos indicando que vamos a usar el un sub data source, del data source principal del reporte.
“detailPath”, es una xpath que indicara la fuente de datos del subreporte.

subreportExpression, contiene la localización del diseño compilado del reporte de detalle, en consecuencia este es el path de un archivo .jasper.

Java

package bo.sumasoftware.hellojasperreports;
 
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.engine.data.JRXmlDataSource;
 
import java.util.HashMap;
 
/**
 * @author renan
 */
public class WriteInvoiceBasic {
    public static void main(String[] args) throws JRException {
        JasperCompileManager.
                compileReportToFile( "src/main/jrxml/invoice.jrxml","invoice.jasper");
        JasperCompileManager.
                compileReportToFile( "src/main/jrxml/invoice-detail.jrxml","invoice-detail.jasper");
 
        JasperReport master = (JasperReport)JRLoader.loadObject("invoice.jasper");
        JRXmlDataSource xmlDataSource = new JRXmlDataSource("src/main/xml/invoice.xml", "/invoice");
        HashMap hashMap = new HashMap();
        hashMap.put("detailPath","/invoice/products/product");
        JasperPrint jasperPrint = JasperFillManager.fillReport(master, hashMap, xmlDataSource);
        JasperExportManager.exportReportToPdfFile(jasperPrint, "invoice-basic.pdf");
    }
}

Compilación de los reportes. Como veran lo primero que hacemos es compilar los reportes maestro y detalle. al compilar estamos creando los respectivos archivos .jasper de cada reporte.
Hacemos esto, ya que en el reporte maestro se necesita pasar un archivo .jasper.

Cargando reporte maestro. Para realizar esto estamos usando la clase JRLoader y su método loadObject para leer el reporte maestro cmpilado invoice.jasper.

Creacion de JRXMLDataSource. Al crear este objeto, le pasamos como parámetro el archivo xml de datos y también una expresión xpath que indica el nodo raiz de datos para el reporte principal.

Definición de parámetros. En un HashMap estamos definiendo un parámetro llamado “detailPath”. detailPath indica contiene una expresión xpath que indica el data source del subreporte.
Esta es la parte importe. Si nos equivocamos en definir este parámetro el subreporte simplemente no mostrar información.
También tenemos que hacer notar que este es un parámetro definido en el diseño del reporte maestro y se hace referencia a este en el elemento dataSourceExpression.

Impresión del reporte. Acá ya solo se llena el reporte y luego se lo imprime en un archivo pdf.

:) bueno, hasta aquí por hoy. :)
pueden bajar el código fuente. Este es un poco diferente pero tiene dos ejemplos, uno básico y otro mas completo.

Renan Huanca JasperReports ,

Comments are closed.