JasperReport Master-Details report with sub-report - jasper-reports

Master record: Course ID, Course Name
Detail record (in sub-report): Course ID (Key to link with Master record), Student ID, Student Name
Example of output:
Course ID: A001
Course Name: Course A
Student ID Student Name
SA001 David
SA002 Marina
Course ID: B001
Course Name: Course B
Student ID Student Name
SB001 Albert
SB002 Jess
How should I design the report and code it? Want to get an idea to design such master-detail report using sub-report instead of grouping.
There are examples of doing this by writing the sql for sub-report with the passing-in parameter, for example:
select * from Table A where courseID = $P!{courseID}
My problem is both the datasource for master and detail report are generated at runtime, for example in my jsp coding:
cs = conn.prepareCall( "{call spTestJasperA (?)}" );
cs1 = conn.prepareCall( "{call spTestJasperB (?)}" );
cs.setInt(1,70);
cs1.setInt(1,70);
rs = cs.executeQuery();
rs1 = cs1.executeQuery();
JRDataSource ds = new JRResultSetDataSource(rs);
JRDataSource ds1 = new JRResultSetDataSource(rs1);
Map<String, Object> parameters = new HashMap<String, Object>();
parameters.put("prmDs1",ds1);
ServletContext context = request.getServletContext();
String s = context.getRealPath("/SubRptA.jasper");
File reportFile = new File(s);
byte[] bytes = JasperRunManager.runReportToPdf(reportFile.getPath(), parameters, ds);
In Master.jrxml:
<detail>
<band height="83" splitType="Prevent">
<subreport>
<reportElement x="0" y="20" width="600" height="63" uuid="2ba47ed7-970d-43f8-9338-ca686c86ca18"/>
<subreportParameter name="prmCourseID">
<subreportParameterExpression><![CDATA[$F{courseID}]]></subreportParameterExpression>
</subreportParameter>
<dataSourceExpression><![CDATA[$P{prmDs1}]]></dataSourceExpression>
<subreportExpression><![CDATA["SubRptA_sub.jasper"]]></subreportExpression>
</subreport>
</band>
</detail>
How to link master and sub-report with the run-time datasource?

Related

How to convert the number of month in the name abbreviation in Jasper Report?

I have a XY Line Chart in JasperReports and I´m using a range in my Chart for display the Months, currently i´m using this configuration and show the values from 1 to 12 in X Line:
Image of currently chart
<?xml version="1.0" encoding="UTF-8"?>
<property name="net.sf.jasperreports.customizer.class.1" value="net.sf.jasperreports.customizers.axis.DomainAxisCustomizer"/>
<property name="net.sf.jasperreports.customizer.1.tickUnit" value="1.0"/>
<propertyExpression name="net.sf.jasperreports.customizer.1.minValue"><![CDATA["1"]]></propertyExpression>
<propertyExpression name="net.sf.jasperreports.customizer.1.maxValue"><![CDATA["12"]]></propertyExpression>
but I need show the abbreviation of the months, like this:
1 - JAN,
2 - FEB,
3 - MAR,
...
Is there a better way to do this?
You can implement your own domain axis customizer like this:
public class DomainAxisWithMonthsCustomizer extends JRAbstractChartCustomizer {
#Override
public void customize(JFreeChart chart, JRChart jasperChart) {
XYPlot plot = chart.getXYPlot();
String[] shortMonths = new DateFormatSymbols().getShortMonths();
SymbolAxis domainAxis = new SymbolAxis("Month",
IntStream.rangeClosed(1, 12)
.mapToObj(m -> m + " - " + shortMonths[m - 1].toUpperCase())
.toArray(String[]::new));
plot.setDomainAxis(domainAxis);
}
}
And use it in your report template:
<chart customizerClass="my.project.report.DomainAxisWithMonthsCustomizer">
...
</chart>

Dynamically red negative numbers in report

All negative numbers in my report will need to be red. Is there any way I can do this through the number formatting? If not, is it possible to create a dynamic conditional somewhere that would check the current text field?
for example:
If this Textfield < 0 then this texfield font is red
Keep in mind, it would need to be dynamic in the sense that I could drop it on all the textfields without changing the condition.
I do understand conditional styles, however rather than using specific fields for the condition (as in the question this is marked for duplicate against) I'd like to allow the condition to be dragged onto any text field and distinguish whether or not it is less than 0.
You cannot achieve this with conditional styles.
The solution would be to create a custom formatter that would output styled text and have the textField configured with markup="styled" like so:
<textField>
<reportElement x="72" y="16" width="100" height="24" uuid="698866c8-7d26-4bc7-8727-b4a56d239a53"/>
<textElement markup="styled"/>
<textFieldExpression><![CDATA[NegativeNumberFormatter.format($F{A1}, "#,##0.###")]]></textFieldExpression>
</textField>
NegativeNumberFormatter might look like this(using default package):
import java.text.DecimalFormat;
public class NegativeNumberFormatter {
public static String format(Number n, String pattern) {
if (n != null) {
if (n.doubleValue() < 0) {
DecimalFormat df = new DecimalFormat();
if (pattern != null) {
df.applyPattern(pattern);
}
return "<font color=\"red\">" + df.format(n) + "</font>";
} else {
return "" + n;
}
}
return null;
}
}

Populating unbound field in crystal report

I have a crystal report with these fields:
-Name
-Other
The field 'Other' is not a database field, so I added it as an unbound field.
Then I have this code in my controller:
DataSet ds = new DataSet();
DataTable dt = new DataTable("Product");
dt.Columns.Add("Name");
dt.Columns.Add("Other");
When I have populated the above datatable I add it to the dataset, anf then set the dataset as report's datasource:
ReportDocument report = new ReportDocument();
string reportFile = Server.MapPath("~/") + "\\bin\\Reports\\MyReport.rpt";
report.Load(reportFile);
report.SetDataSource(ds);
Then I set the export options (it's a excel report).
In my report I'm getting the text for the 'Name' field correctly, but the 'Other' field is empty. How can I associate the column from the datatable to this field?
Thanks in advance.
This is aspx File :
<CR:CrystalReportViewer ID="CrystalReportViewer1" runat="server" EnableParameterPrompt="false"
ReuseParameterValuesOnRefresh="true" EnableDatabaseLogonPrompt="False" ToolPanelView="None"
HasToggleParameterPanelButton="false" HasCrystalLogo="False" />
This is Cs File ......
protected void btnSubmit_Click(object sender, EventArgs e)
{
LoadData();
}
protected void LoadData()
{
DataSet ds= null;
ds = new DataSet();
DataTable dt = new DataTable("Product");
dt.Columns.Add("Name");
dt.Columns.Add("Other");
rptDoc.Load(Server.MapPath("~/bin/Reports/MyReport.rpt"));
rptDoc.SetDataSource(ds);
CrystalReportViewer1.ReportSource = rptDoc;
CrystalReportViewer1.DataBind();
CrystalReportViewer1.refreshreport();
}
Try this code.......
try this with VB
'pass texbox2 to unbounddate1
CrystalReportSource1.ReportDocument.DataDefinition.FormulaFields.Item("unbounddate1").Text = "'" & Me.TextBox2.Text & "'"
'pass texbox3 to unbounddate2 CrystalReportSource1.ReportDocument.DataDefinition.FormulaFields.Item("unbounddate2").Text = "'" & Me.TextBox3.Text & "'"

Entity Framework Procedure Error - Silverlight, EF, Oracle

Error Message:
The data reader returned by the store data provider does not have enough columns for the query requested.
public ObjectResult<global::System.String> P_GET_MST_CODE(global::System.String i_RES_TYPE, ObjectParameter v_RESULT)
{
ObjectParameter i_RES_TYPEParameter;
if (i_RES_TYPE != null)
{
i_RES_TYPEParameter = new ObjectParameter("I_RES_TYPE", i_RES_TYPE);
}
else
{
i_RES_TYPEParameter = new ObjectParameter("I_RES_TYPE", typeof(global::System.String));
}
return base.ExecuteFunction<global::System.String>("P_GET_MST_CODE", i_RES_TYPEParameter, v_RESULT);
}
Below is the definition of the stored procedure.
<Function Name="P_GET_MST_CODE" Aggregate="false" BuiltIn="false" NiladicFunction="false" IsComposable="false" ParameterTypeSemantics="AllowImplicitConversion" Schema="LEGACY">
<Parameter Name="I_RES_TYPE" Type="varchar2" Mode="In" />
<Parameter Name="V_RESULT" Type="varchar2" Mode="Out" />
</Function>
Can anyone help me to solve this problem?
I've solved this by avoiding entity connection object. :-)
Until now, It seems to be unsupported for OUT Parameter of ORACLE Database.
Below is the changed code example.
using System.Data;
using System.Data.Common;
using System.Data.EntityClient;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Server;
using Oracle.DataAccess.Client;
.......
[Invoke]
public string method(string OTL)
{
DbCommand cmd = (((EntityConnection)this.ObjectContext.Connection).StoreConnection).CreateCommand();
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.CommandText = "LoveMe";
OracleParameter ep = new OracleParameter("I_RES_TYPE", OTL);
ep.OracleDbType = OracleDbType.Varchar2;
ep.Direction = ParameterDirection.Input;
OracleParameter epV_RESULT = new OracleParameter("V_RESULT", null);
epV_RESULT.OracleDbType = OracleDbType.Varchar2;
epV_RESULT.Size = 30;
epV_RESULT.Direction = ParameterDirection.Output;
cmd.Parameters.Add(ep);
cmd.Parameters.Add(epV_RESULT);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
string result = cmd.Parameters[1].Value.ToString(); //What I want to get.
cmd.Connection.Close();
return result;
}

How to merge two pdf documents into a single report in JasperReports?

I am new to JasperReports. I can create a simple PDF document with Javabean datasource. In my project I have created two separate pdf document with separate javabean datasource. Now I want to merge both documents into a single document. Can anyone tell me how to merge both documents into single document using JasperReports?
unfortunately the solution is build a sub report and use the 2 different DataSource or what ever connection you used
but there is an easy way to get over with this question :D
just simple no new reports ..... Voilà
ok lets do it
JasperPrint jp1 = JasperFillManager.fillReport(url.openStream(), parameters,
new JRBeanCollectionDataSource(inspBean));
JasperPrint jp2 = JasperFillManager.fillReport(url.openStream(), parameters,
new JRBeanCollectionDataSource(inspBean));
ok we have over 2 records ..lets take our first record jp1 and add jp2 content into it
List pages = jp2 .getPages();
for (int j = 0; j < pages.size(); j++) {
JRPrintPage object = (JRPrintPage)pages.get(j);
jp1.addPage(object);
}
JasperViewer.viewReport(jp1,false);
This work like a charm .. with couple of loops you can merge any number of report together .. without creating new reports
http://lnhomez.blogspot.com/2011/11/merge-multiple-jasper-reports-in-to.html
You can use subreports for this. You dont have to recreate your current reports.
Create a master report, with 0 margins. Add all your reports to this as subreport and put condition that if datasource is available for this, only then print this report.
Now put all your individual datasources into one map data source and pass this datasource to master report.
Configute all subreports to the key in the map.
You can use use a list of JasperPrint like this:
List<JasperPrint> jasperPrintList = new ArrayList<JasperPrint>();
jasperPrintList.add(JasperFillManager.fillReport("Report_file1.jasper", getReportMap(1), new JREmptyDataSource()));
jasperPrintList.add(JasperFillManager.fillReport("Report_file2.jasper", getReportMap(2), new JREmptyDataSource()));
jasperPrintList.add(JasperFillManager.fillReport("Report_file3.jasper", getReportMap(3), new JREmptyDataSource()));
JRPdfExporter exporter = new JRPdfExporter();
exporter.setExporterInput(SimpleExporterInput.getInstance(jasperPrintList));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput("Report_PDF.pdf"));
SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();
configuration.setCreatingBatchModeBookmarks(true);
exporter.setConfiguration(configuration);
exporter.exportReport();
Multiple Pages in one JasperPrint
Sample Code:
DefaultTableModel dtm = new DefaultTableModel(new Object[0][3], new String[]{"Id","Name","Family"});
String[] fields= new String[3];
boolean firstFlag=true;
JasperPrint jp1 =null;
JasperPrint jp2 =null;
for (int i=0 ; i<=pagesCount ; i++)
{
fields[0]= "id";
fields[1]= "name";
fields[2]= "family";
dtm.insertRow(0, fields);
try
{
Map<String, Object> params = new HashMap<String, Object>();
if (firstFlag)
{
jp1 = JasperFillManager.fillReport(getClass().getResourceAsStream(reportsource), params, new JRTableModelDataSource(dtm));
firstFlag=false;
}else
{
jp2 = JasperFillManager.fillReport(getClass().getResourceAsStream(reportsource), params, new JRTableModelDataSource(dtm));
jp1.addPage(jp2.getPages().get(0));
}
}catch (Exception e)
{
System.out.println(e.fillInStackTrace().getMessage());
}
}
JasperViewer.viewReport(jp1,false);
Lahiru Nirmal's answer was simple and to the point. Here's a somewhat expanded version that also copies styles and other things (not all of which I'm positive are crucial).
Note that all of the pages are of the same size.
public static JasperPrint createJasperReport(String name, JasperPrint pattern)
{
JasperPrint newPrint = new JasperPrint();
newPrint.setBottomMargin(pattern.getBottomMargin());
newPrint.setLeftMargin(pattern.getLeftMargin());
newPrint.setTopMargin(pattern.getTopMargin());
newPrint.setRightMargin(pattern.getRightMargin());
newPrint.setLocaleCode(pattern.getLocaleCode());
newPrint.setName(name);
newPrint.setOrientation(pattern.getOrientationValue());
newPrint.setPageHeight(pattern.getPageHeight());
newPrint.setPageWidth(pattern.getPageWidth());
newPrint.setTimeZoneId(pattern.getTimeZoneId());
return newPrint;
}
public static void addJasperPrint(JasperPrint base, JasperPrint add)
{
for (JRStyle style : add.getStyles())
{
String styleName = style.getName();
if (!base.getStylesMap().containsKey(styleName))
{
try
{
base.addStyle(style);
}
catch (JRException e)
{
logger.log(Level.WARNING, "Couldn't add a style", e);
}
}
else
logger.log(Level.FINE, "Dropping duplicate style: " + styleName);
}
for (JRPrintPage page : add.getPages())
base.addPage(page);
if (add.hasProperties())
{
JRPropertiesMap propMap = add.getPropertiesMap();
for (String propName : propMap.getPropertyNames())
{
String propValue = propMap.getProperty(propName);
base.setProperty(propName, propValue);
}
}
if (add.hasParts())
{
PrintParts parts = add.getParts();
Iterator<Entry<Integer, PrintPart>> partsIterator = parts.partsIterator();
while (partsIterator.hasNext())
{
Entry<Integer, PrintPart> partsEntry = partsIterator.next();
base.addPart(partsEntry.getKey(), partsEntry.getValue());
}
}
List<PrintBookmark> bookmarks = add.getBookmarks();
if (bookmarks != null)
for (PrintBookmark bookmark : bookmarks)
base.addBookmark(bookmark);
}
Then, to use it:
JasperPrint combinedPrint = createJasperReport("Multiple Reports",
print1);
for (JasperPrint addPrint : new JasperPrint[] { print1, print2, print3 })
addJasperPrint(combinedPrint, addPrint);
// Now do whatever it was you'd do with the JasperPrint.
String combinedXml = JasperExportManager.exportReportToXml(combinedPrint);
I see JasperReports now has a newer "Report Book" feature that might be a better solution, but I haven't used one yet.