Fixed Hourly MIS

This commit is contained in:
2026-08-12 15:40:43 +05:30
parent 61bea5fb6e
commit a757799724
24 changed files with 996 additions and 30 deletions

View File

@@ -0,0 +1,49 @@
package lib.models;
import java.time.LocalDate;
import java.util.Arrays;
public final class HourlyMisRecord {
private final long userId;
private final String displayName;
private final LocalDate receivedDate;
private final int[] hours = new int[24];
public HourlyMisRecord(long userId, String displayName, LocalDate receivedDate) {
this.userId = userId;
this.displayName = displayName;
this.receivedDate = receivedDate;
}
public long getUserId() { return userId; }
public String getDisplayName() { return displayName; }
public LocalDate getReceivedDate() { return receivedDate; }
public int getHour(int hour) { return hours[hour]; }
public void addCases(int hour, int cases) { hours[hour] += cases; }
public int getTotal() { return Arrays.stream(hours).sum(); }
public int getHour00() { return hours[0]; }
public int getHour01() { return hours[1]; }
public int getHour02() { return hours[2]; }
public int getHour03() { return hours[3]; }
public int getHour04() { return hours[4]; }
public int getHour05() { return hours[5]; }
public int getHour06() { return hours[6]; }
public int getHour07() { return hours[7]; }
public int getHour08() { return hours[8]; }
public int getHour09() { return hours[9]; }
public int getHour10() { return hours[10]; }
public int getHour11() { return hours[11]; }
public int getHour12() { return hours[12]; }
public int getHour13() { return hours[13]; }
public int getHour14() { return hours[14]; }
public int getHour15() { return hours[15]; }
public int getHour16() { return hours[16]; }
public int getHour17() { return hours[17]; }
public int getHour18() { return hours[18]; }
public int getHour19() { return hours[19]; }
public int getHour20() { return hours[20]; }
public int getHour21() { return hours[21]; }
public int getHour22() { return hours[22]; }
public int getHour23() { return hours[23]; }
}

View File

@@ -0,0 +1,6 @@
package lib.models;
public record ReportColumnDefinition(
String columnKey, String label, int displayOrder, String alignment,
int width, boolean visible, boolean totalled) {
}

View File

@@ -0,0 +1,8 @@
package lib.models;
import java.util.List;
public record ReportDefinition(
String reportKey, String title, boolean hideLeadingZeroColumns,
List<ReportColumnDefinition> columns) {
}

View File

@@ -10,7 +10,7 @@
<link href="/matrix/css/matrix-v2.css?v=6" rel="stylesheet">
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet">
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet">
<link href="/matrix/css/dedupe-workflows-v1.css?v=3" rel="stylesheet">
<link href="/matrix/css/dedupe-workflows-v1.css?v=12" rel="stylesheet">
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet">
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet">
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
@@ -23,7 +23,7 @@
data-context-path="${pageContext.request.contextPath}">
<div id="PageFrame">
<%@ include file="/WEB-INF/app/fragments/app-shell-header.jspf" %>
<main class="matrix-tool-workspace__content">
<main id="formcontainer" class="matrix-tool-workspace__content">
<form:form method="post" action="${pageContext.request.contextPath}/ver/dedupecaselist"
id="dedupeWorkspace" modelAttribute="dedupeWorkspace">
<section class="matrix-dedupe-worklist">

View File

@@ -27,7 +27,7 @@
<link href="/matrix/css/matrix-v2.css?v=6" rel="stylesheet" type="text/css" />
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet" type="text/css" />
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
<link href="/matrix/css/dedupe-workflows-v1.css?v=8" rel="stylesheet" type="text/css" />
<link href="/matrix/css/dedupe-workflows-v1.css?v=12" rel="stylesheet" type="text/css" />
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />

View File

@@ -212,7 +212,7 @@
<div id="FormPanel2" class="FormPanel">
<table align="center" class="matrix-form-controls" style="width:100%">
<tr>
<td><form:textarea path="earremarks" style="width:98%" rows="9" readonly="true" ></form:textarea></td>
<td><form:textarea path="earremarks" style="width:98%" rows="8" readonly="true" ></form:textarea></td>
</tr>
</table>
</div>
@@ -232,7 +232,7 @@
<div id="FormPanel2" class="FormPanel">
<table align="center" class="matrix-form-controls" style="width:100%">
<tr>
<td><form:textarea path="negremarks" style="width:98%" rows="9" readonly="true" ></form:textarea></td>
<td><form:textarea path="negremarks" style="width:98%" rows="8" readonly="true" ></form:textarea></td>
</tr>
</table>
</div>

View File

@@ -219,7 +219,7 @@
DOB
</div>
<div class="inputcontainer">
<input type="text" id="dob" name="dob" maxlength="10" style="width:80px" value="" onkeydown="return addSlashes(this,event)" onblur="validate(this,'f','Date')" />
<input type="text" id="dob" name="dob" class="matrix-date-input" data-date-mode="date" maxlength="10" style="width:80px" value="" autocomplete="off" inputmode="numeric" placeholder="DD/MM/YYYY" onkeydown="return addSlashes(this,event)" onblur="validate(this,'f','Date')" />
</div>
</div>
<div class="widget">

View File

@@ -27,11 +27,13 @@
<link href="/matrix/css/matrix-shell-v2.css?v=7" rel="stylesheet" type="text/css" />
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
<link href="/matrix/css/mis-filter-v4.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/date-time-input-v1.css?v=1" rel="stylesheet" type="text/css" />
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
<link href="/matrix/css/matrix-frame-dialog.css?v=6" rel="stylesheet" type="text/css" />
<script src="/matrix/js/matrix-frame-dialog.js?v=2" defer></script>
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
<script src="/matrix/js/lib/date-time-input-v1.js?v=1" defer></script>
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
</head>

View File

@@ -17,7 +17,7 @@
<link href="/matrix/css/bootstrap-5.3.8.min.css" rel="stylesheet" type="text/css" />
<link href="/matrix/css/matrix-v2.css?v=6" rel="stylesheet" type="text/css" />
<link href="/matrix/css/tool-workspace-v3.css?v=2" rel="stylesheet" type="text/css" />
<link href="/matrix/css/mis-output-v1.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/mis-output-v1.css?v=7" rel="stylesheet" type="text/css" />
<title>Cygnus 1.0 | ${filter.title }</title>
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
@@ -25,7 +25,16 @@
</head>
<body class="matrix-v2 matrix-mis-output">
<form:form method="post" name="filter" id="filter" modelAttribute="filter" >
<c:if test="${hourlyJasperReport}">
<div class="matrix-report-actions">
<button type="submit" class="btn btn-primary btn-sm" formaction="${pageContext.request.contextPath}/ver/dtmis/xlsx">Export XLSX</button>
<button type="submit" class="btn btn-outline-primary btn-sm" formaction="${pageContext.request.contextPath}/ver/dtmis/pdf">Export PDF</button>
<button type="submit" class="btn btn-outline-primary btn-sm" formaction="${pageContext.request.contextPath}/ver/dtmis/csv">Export CSV</button>
</div>
</c:if>
<div class="matrix-report-scroll">
${filter.html}
</div>
<input type="hidden" id="invalidfields" value="0" />
<form:hidden path="qid" />
<form:hidden path="title" />

View File

@@ -1,6 +1,11 @@
.matrix-tool-workspace.matrix-dedupe-workflow {
padding: 0;
}
.matrix-dedupe-workflow .matrix-tool-workspace__content {
width: calc(100% - 24px) !important;
max-width: 1380px !important;
margin: 10px auto 0 !important;
}
.matrix-dedupe-workflow .title {
@@ -109,9 +114,10 @@
}
.matrix-dedupe-worklist__table-wrap {
margin: 8px;
border: 1px solid #c3d0da;
border-radius: 4px;
margin: 0;
border: 0;
border-top: 1px solid #c3d0da;
border-radius: 0;
}
.matrix-dedupe-worklist__table-wrap thead th {

View File

@@ -10,7 +10,7 @@
.matrix-mis-output form {
width: 100%;
max-width: 1600px;
max-width: none;
margin: 0 auto;
padding: 12px;
overflow: auto;
@@ -129,3 +129,43 @@
box-shadow: none;
}
}
.matrix-report-actions {
display: flex;
justify-content: flex-end;
gap: 6px;
margin: 0 0 0.5rem;
}
.matrix-report-scroll {
width: 100%;
max-width: 100%;
overflow-x: auto;
overflow-y: hidden;
padding-bottom: 6px;
}
.matrix-report-scroll .jrPage {
max-width: none;
}
.matrix-mis-output .jrPage {
margin: 0 auto;
background: #f7fafc;
border: 1px solid #9fb2c3;
box-shadow: 0 2px 8px rgba(38, 66, 89, 0.12);
}
.hourly-mis { width: 100%; box-sizing: border-box; padding: 18px; background: #f7fafc; border: 1px solid #9fb2c3; }
.hourly-mis__header { text-align: center; }
.hourly-mis__header h1 { margin: 0; font-size: 18px; font-weight: 700; }
.hourly-mis__header h2 { margin: 3px 0; font-size: 14px; font-weight: 700; }
.hourly-mis__header p, .hourly-mis__branch { margin: 5px 0; font-size: 14px; }
.hourly-mis__branch { font-weight: 700; }
.hourly-mis__table { width: 100%; table-layout: fixed; border-collapse: collapse; background: #fff; }
.hourly-mis__table th, .hourly-mis__table td { padding: 5px 4px; border: 1px solid #9fb2c3; font-size: 12px; text-align: center; }
.hourly-mis__table thead th { background: #dce7f1; font-weight: 700; }
.hourly-mis__table th:first-child, .hourly-mis__table td:first-child { width: 110px; text-align: left; }
.hourly-mis__operator th { background: #e3e8ed; text-align: left; }
.hourly-mis__operator span { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hourly-mis__total th, .hourly-mis__total td { background: #dde8f1; font-weight: 700; }
.hourly-mis__empty { padding: 28px !important; color: #73808b; font-style: italic; text-align: center !important; }

View File

@@ -43,6 +43,17 @@
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>${jasperreports.version}</version>
<exclusions>
<exclusion>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>

View File

@@ -10,6 +10,7 @@ import matrix.nimble.mis.model.Filters;
import matrix.nimble.mis.model.MISHandler;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.GlobalClass;
import matrix.services.mis.HourlyMisService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
@@ -17,10 +18,19 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
@Controller
@SessionAttributes({"Sessvals"})
public class MIS {
private final HourlyMisService hourlyMisService;
public MIS(HourlyMisService hourlyMisService) {
this.hourlyMisService = hourlyMisService;
}
@RequestMapping(value="datetimerange",method=RequestMethod.POST )
public String DateRangeFilter(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,HttpSession session)
{
@@ -84,6 +94,13 @@ public class MIS {
@RequestMapping(value="dtmis",method=RequestMethod.POST )
public String DateRangeMIS(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,@ModelAttribute(value="filter") Filters filter,HttpSession session)
{
if (filter.getQid() == HourlyMisService.QUERY_ID) {
filter.setHtml(hourlyMisService.renderHtml(filter, Sessvals));
model.addAttribute("hourlyJasperReport", true);
model.addAttribute("filter", filter);
model.addAttribute("Sessvals", Sessvals);
return "mis/misreport";
}
MISHandler MH=new MISHandler();
MH.setProcessFlag(true);
MH.setErrCode("5101");
@@ -93,6 +110,49 @@ public class MIS {
model.addAttribute("Sessvals",Sessvals);
return "mis/misreport";
}
@RequestMapping(value="dtmis/xlsx",method=RequestMethod.POST )
@ResponseBody
public ResponseEntity<byte[]> DateRangeMISXlsx(
@ModelAttribute(value="Sessvals") Session Sessvals,
@ModelAttribute(value="filter") Filters filter) {
if (filter.getQid() != HourlyMisService.QUERY_ID) {
return ResponseEntity.badRequest().build();
}
byte[] report = hourlyMisService.renderXlsx(filter, Sessvals);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=hourly-mis.xlsx")
.contentType(MediaType.parseMediaType(
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"))
.body(report);
}
@RequestMapping(value="dtmis/pdf",method=RequestMethod.POST )
@ResponseBody
public ResponseEntity<byte[]> DateRangeMISPdf(
@ModelAttribute(value="Sessvals") Session Sessvals,
@ModelAttribute(value="filter") Filters filter) {
if (filter.getQid() != HourlyMisService.QUERY_ID) return ResponseEntity.badRequest().build();
return download(hourlyMisService.renderPdf(filter, Sessvals), "hourly-mis.pdf",
MediaType.APPLICATION_PDF);
}
@RequestMapping(value="dtmis/csv",method=RequestMethod.POST )
@ResponseBody
public ResponseEntity<byte[]> DateRangeMISCsv(
@ModelAttribute(value="Sessvals") Session Sessvals,
@ModelAttribute(value="filter") Filters filter) {
if (filter.getQid() != HourlyMisService.QUERY_ID) return ResponseEntity.badRequest().build();
return download(hourlyMisService.renderCsv(filter, Sessvals), "hourly-mis.csv",
MediaType.parseMediaType("text/csv"));
}
private ResponseEntity<byte[]> download(byte[] content, String filename, MediaType mediaType) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename)
.contentType(mediaType)
.body(content);
}
@RequestMapping(value="dtusermis",method=RequestMethod.POST )
public String DateRangeUserMIS(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,@ModelAttribute(value="filter") Filters filter,HttpSession session)
{

View File

@@ -499,13 +499,13 @@ public class MISHandler {
CtrlMTimeRange+="<div class=\"widget\">";
CtrlMTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> From Date</div>";
CtrlMTimeRange+="<div class=\"inputcontainer\">";
CtrlMTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','DateTime')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Fromdate\" name=\"Fromdate\" value=\""+frmdate+" 00:00:00\" />";
CtrlMTimeRange+=dateInput("Fromdate", frmdate + " 00:00:00", "datetime", "DateTime", 19);
CtrlMTimeRange+="</div>";
CtrlMTimeRange+="</div>";
CtrlMTimeRange+="<div class=\"widget\">";
CtrlMTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> To Date</div>";
CtrlMTimeRange+="<div class=\"inputcontainer\">";
CtrlMTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','DateTime')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Todate\" name=\"Todate\" value=\""+todate+" 23:59:59\" />";
CtrlMTimeRange+=dateInput("Todate", todate + " 23:59:59", "datetime", "DateTime", 19);
CtrlMTimeRange+="</div>";
CtrlMTimeRange+="</div>";
return CtrlMTimeRange;
@@ -517,13 +517,13 @@ public class MISHandler {
CtrlDateTimeRange+="<div class=\"widget\">";
CtrlDateTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> From Date</div>";
CtrlDateTimeRange+="<div class=\"inputcontainer\">";
CtrlDateTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','DateTime')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Fromdate\" name=\"Fromdate\" value=\""+(frmdate+" 00:00:00")+"\" />";
CtrlDateTimeRange+=dateInput("Fromdate", frmdate + " 00:00:00", "datetime", "DateTime", 19);
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="<div class=\"widget\">";
CtrlDateTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> To Date</div>";
CtrlDateTimeRange+="<div class=\"inputcontainer\">";
CtrlDateTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','DateTime')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Todate\" name=\"Todate\" value=\""+(frmdate+" 23:59:59")+"\" />";
CtrlDateTimeRange+=dateInput("Todate", frmdate + " 23:59:59", "datetime", "DateTime", 19);
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="</div>";
return CtrlDateTimeRange;
@@ -535,17 +535,26 @@ public class MISHandler {
CtrlDateTimeRange+="<div class=\"widget\">";
CtrlDateTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> From Date</div>";
CtrlDateTimeRange+="<div class=\"inputcontainer\">";
CtrlDateTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','Date')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Fromdate\" name=\"Fromdate\" value=\""+(frmdate)+"\" />";
CtrlDateTimeRange+=dateInput("Fromdate", frmdate, "date", "Date", 10);
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="<div class=\"widget\">";
CtrlDateTimeRange+="<div class=\"lblcontainer\" style=\"width:85px\"><font color=\"red\">*</font> To Date</div>";
CtrlDateTimeRange+="<div class=\"inputcontainer\">";
CtrlDateTimeRange+="<input type=\"text\" onblur=\"validate(this,'t','Date')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"19\" id=\"Todate\" name=\"Todate\" value=\""+(frmdate)+"\" />";
CtrlDateTimeRange+=dateInput("Todate", frmdate, "date", "Date", 10);
CtrlDateTimeRange+="</div>";
CtrlDateTimeRange+="</div>";
return CtrlDateTimeRange;
}
private String dateInput(String id, String value, String mode, String validator, int maxlength)
{
return "<input type=\"text\" class=\"matrix-date-input\" data-date-mode=\"" + mode
+ "\" autocomplete=\"off\" inputmode=\"numeric\" placeholder=\""
+ ("datetime".equals(mode) ? "DD/MM/YYYY HH:MM:SS" : "DD/MM/YYYY")
+ "\" onblur=\"validate(this,'t','" + validator
+ "')\" onkeydown=\"return addSlashes(this,event)\" maxlength=\"" + maxlength
+ "\" id=\"" + id + "\" name=\"" + id + "\" value=\"" + value + "\" />";
}
private String FromDateToTimeControl()
{
String frmdate=GlobalClass.DateTime("dd/MM/yyyy", new java.util.Date());

View File

@@ -0,0 +1,405 @@
package matrix.services.mis;
import lib.models.HourlyMisRecord;
import lib.models.ReportColumnDefinition;
import lib.models.ReportDefinition;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.FontFactory;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRPrintElement;
import net.sf.jasperreports.engine.JRPrintPage;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
import net.sf.jasperreports.engine.export.JRCsvExporter;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.export.ooxml.JRXlsxExporter;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimpleWriterExporterOutput;
import net.sf.jasperreports.export.SimpleXlsxReportConfiguration;
import org.springframework.stereotype.Component;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.FillPatternType;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@Component
public final class HourlyMisReportRenderer {
private static final int DATE_COLUMN_WIDTH = 105;
private static final int HOUR_COLUMN_WIDTH = 55;
private static final int REPORT_CONTENT_WIDTH = 1510;
private static final int FULL_CONTENT_WIDTH = 1690;
private final JasperReport report;
public HourlyMisReportRenderer() {
try (InputStream template = getClass().getResourceAsStream("/reports/hourly-mis.jrxml")) {
if (template == null) throw new IllegalStateException("Hourly MIS template is missing");
report = JasperCompileManager.compileReport(template);
} catch (Exception exception) {
throw new IllegalStateException("Unable to compile Hourly MIS template", exception);
}
}
public String html(List<HourlyMisRecord> rows, Map<String, Object> parameters,
ReportDefinition definition) {
int firstHour = definition.hideLeadingZeroColumns() ? firstActiveHour(rows) : 0;
Map<String, ReportColumnDefinition> columns = definition.columns().stream()
.filter(ReportColumnDefinition::visible)
.collect(java.util.stream.Collectors.toMap(
ReportColumnDefinition::columnKey, column -> column));
int visibleColumns = 1 + (columns.containsKey("total") ? 1 : 0);
for (int hour = firstHour; hour < 24; hour++) {
if (columns.containsKey("hour" + hour)) visibleColumns++;
}
StringBuilder html = new StringBuilder(4096);
html.append("<section class=\"hourly-mis\"><header class=\"hourly-mis__header\"><h1>")
.append(escape(parameters.get("COMPANY_NAME"))).append("</h1><h2>")
.append(escape(definition.title())).append("</h2><p>From: ")
.append(escape(parameters.get("FROM_DATE"))).append(" &nbsp; To: ")
.append(escape(parameters.get("TO_DATE"))).append("</p></header><div class=\"hourly-mis__branch\">Branch: ")
.append(escape(parameters.get("BRANCH_NAME"))).append("</div>")
.append("<table class=\"hourly-mis__table\"><thead><tr><th>")
.append(escape(label(columns, "date", "Date"))).append("</th>");
for (int hour = firstHour; hour < 24; hour++) {
if (columns.containsKey("hour" + hour)) html.append("<th>")
.append(escape(label(columns, "hour" + hour, String.valueOf(hour)))).append("</th>");
}
if (columns.containsKey("total")) html.append("<th>")
.append(escape(label(columns, "total", "Total"))).append("</th>");
html.append("</tr></thead><tbody>");
if (rows.isEmpty()) {
return html.append("<tr><td class=\"hourly-mis__empty\" colspan=\"")
.append(visibleColumns)
.append("\">No Records Found</td></tr></tbody></table></section>")
.toString();
}
String currentUser = null;
int[] totals = new int[24];
int grandTotal = 0;
for (HourlyMisRecord row : rows) {
if (!row.getDisplayName().equals(currentUser)) {
if (currentUser != null) {
appendTotalRow(html, totals, grandTotal, firstHour, columns);
totals = new int[24];
grandTotal = 0;
}
currentUser = row.getDisplayName();
html.append("<tr class=\"hourly-mis__operator\"><th colspan=\"")
.append(visibleColumns).append("\" title=\"")
.append(escape(currentUser)).append("\"><span>")
.append(escape(currentUser)).append("</span></th></tr>");
}
html.append("<tr><td>").append(row.getReceivedDate()).append("</td>");
for (int hour = firstHour; hour < 24; hour++) {
if (!columns.containsKey("hour" + hour)) continue;
int value = row.getHour(hour);
totals[hour] += value;
html.append("<td>").append(value).append("</td>");
}
grandTotal += row.getTotal();
if (columns.containsKey("total")) html.append("<td>").append(row.getTotal()).append("</td>");
html.append("</tr>");
}
appendTotalRow(html, totals, grandTotal, firstHour, columns);
return html.append("</tbody></table></section>").toString();
}
private String label(Map<String, ReportColumnDefinition> columns, String key, String fallback) {
ReportColumnDefinition column = columns.get(key);
return column == null || column.label() == null || column.label().isBlank()
? fallback : column.label();
}
private void appendTotalRow(StringBuilder html, int[] totals, int grandTotal, int firstHour,
Map<String, ReportColumnDefinition> columns) {
html.append("<tr class=\"hourly-mis__total\"><th>Total</th>");
for (int hour = firstHour; hour < 24; hour++) {
if (columns.containsKey("hour" + hour)) html.append("<td>").append(totals[hour]).append("</td>");
}
if (columns.containsKey("total")) html.append("<td>").append(grandTotal).append("</td>");
html.append("</tr>");
}
private String escape(Object value) {
return String.valueOf(value == null ? "" : value).replace("&", "&amp;")
.replace("<", "&lt;").replace(">", "&gt;").replace("\"", "&quot;")
.replace("'", "&#39;");
}
public byte[] xlsx(List<HourlyMisRecord> rows, Map<String, Object> parameters) {
int firstHour = firstActiveHour(rows);
int columnCount = 26 - firstHour;
try (Workbook workbook = new XSSFWorkbook();
ByteArrayOutputStream output = new ByteArrayOutputStream()) {
var sheet = workbook.createSheet("hourly_mis");
CellStyle title = excelStyle(workbook, true, 14, null);
CellStyle subtitle = excelStyle(workbook, true, 11, null);
CellStyle text = excelStyle(workbook, false, 10, null);
CellStyle header = excelStyle(workbook, true, 10, "DCE7F1");
CellStyle group = excelStyle(workbook, true, 10, "E3E8ED");
CellStyle total = excelStyle(workbook, true, 10, "DDE8F1");
mergedCell(sheet, 0, 0, columnCount - 1, parameters.get("COMPANY_NAME"), title);
mergedCell(sheet, 1, 0, columnCount - 1, parameters.get("REPORT_TITLE"), subtitle);
mergedCell(sheet, 2, 0, columnCount - 1,
"From: " + parameters.get("FROM_DATE") + " To: " + parameters.get("TO_DATE"), text);
mergedCell(sheet, 4, 0, columnCount - 1, "Branch: " + parameters.get("BRANCH_NAME"), text);
Row headings = sheet.createRow(5);
excelCell(headings, 0, "Date", header);
int column = 1;
for (int hour = firstHour; hour < 24; hour++) excelCell(headings, column++, hour, header);
excelCell(headings, column, "Total", header);
writeExcelRows(sheet, rows, firstHour, group, text, total);
sheet.setColumnWidth(0, 16 * 256);
for (int index = 1; index < columnCount; index++) sheet.setColumnWidth(index, 9 * 256);
sheet.createFreezePane(1, 6);
workbook.write(output);
return output.toByteArray();
} catch (Exception exception) {
throw new IllegalStateException("Unable to export Hourly MIS", exception);
}
}
public byte[] pdf(List<HourlyMisRecord> rows, Map<String, Object> parameters) {
int firstHour = firstActiveHour(rows);
int columnCount = 26 - firstHour;
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
Document document = new Document(PageSize.A3.rotate(), 28, 28, 24, 24);
PdfWriter.getInstance(document, output);
document.open();
document.add(centered(parameters.get("COMPANY_NAME"), 15, true));
document.add(centered(parameters.get("REPORT_TITLE"), 11, true));
document.add(centered("From: " + parameters.get("FROM_DATE") + " To: "
+ parameters.get("TO_DATE"), 9, false));
document.add(new Paragraph("Branch: " + parameters.get("BRANCH_NAME"), pdfFont(9, true)));
document.add(new Paragraph(" ", pdfFont(4, false)));
PdfPTable table = new PdfPTable(columnCount);
table.setWidthPercentage(100);
float[] widths = new float[columnCount];
widths[0] = 1.7f;
java.util.Arrays.fill(widths, 1, columnCount - 1, 1f);
widths[columnCount - 1] = 1.2f;
table.setWidths(widths);
pdfCell(table, "Date", true, "DCE7F1", 1);
for (int hour = firstHour; hour < 24; hour++) pdfCell(table, String.valueOf(hour), true, "DCE7F1", 1);
pdfCell(table, "Total", true, "DCE7F1", 1);
writePdfRows(table, rows, firstHour, columnCount);
document.add(table);
document.close();
return output.toByteArray();
} catch (Exception exception) {
throw new IllegalStateException("Unable to export Hourly MIS as PDF", exception);
}
}
private void writeExcelRows(org.apache.poi.ss.usermodel.Sheet sheet, List<HourlyMisRecord> records,
int firstHour, CellStyle groupStyle, CellStyle rowStyle, CellStyle totalStyle) {
int rowIndex = 6;
String user = null;
int[] totals = new int[24];
int grandTotal = 0;
for (HourlyMisRecord record : records) {
if (!record.getDisplayName().equals(user)) {
if (user != null) rowIndex = excelTotal(sheet, rowIndex, totals, grandTotal, firstHour, totalStyle);
user = record.getDisplayName();
totals = new int[24];
grandTotal = 0;
mergedCell(sheet, rowIndex++, 0, 25 - firstHour, user, groupStyle);
}
Row row = sheet.createRow(rowIndex++);
excelCell(row, 0, record.getReceivedDate().toString(), rowStyle);
int column = 1;
for (int hour = firstHour; hour < 24; hour++) {
int value = record.getHour(hour);
totals[hour] += value;
excelCell(row, column++, value, rowStyle);
}
grandTotal += record.getTotal();
excelCell(row, column, record.getTotal(), rowStyle);
}
if (user != null) excelTotal(sheet, rowIndex, totals, grandTotal, firstHour, totalStyle);
}
private int excelTotal(org.apache.poi.ss.usermodel.Sheet sheet, int rowIndex, int[] totals,
int grandTotal, int firstHour, CellStyle style) {
Row row = sheet.createRow(rowIndex++);
excelCell(row, 0, "Total", style);
int column = 1;
for (int hour = firstHour; hour < 24; hour++) excelCell(row, column++, totals[hour], style);
excelCell(row, column, grandTotal, style);
return rowIndex;
}
private CellStyle excelStyle(Workbook workbook, boolean bold, int fontSize, String color) {
CellStyle style = workbook.createCellStyle();
var font = workbook.createFont();
font.setBold(bold);
font.setFontHeightInPoints((short) fontSize);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
style.setBorderTop(BorderStyle.THIN);
style.setBorderRight(BorderStyle.THIN);
style.setBorderBottom(BorderStyle.THIN);
style.setBorderLeft(BorderStyle.THIN);
if (color != null) {
style.setFillForegroundColor(org.apache.poi.ss.usermodel.IndexedColors.valueOf(
color.equals("DCE7F1") ? "LIGHT_CORNFLOWER_BLUE" : "PALE_BLUE").getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
}
return style;
}
private void mergedCell(org.apache.poi.ss.usermodel.Sheet sheet, int rowIndex, int firstColumn,
int lastColumn, Object value, CellStyle style) {
Row row = sheet.createRow(rowIndex);
excelCell(row, firstColumn, value, style);
if (lastColumn > firstColumn) sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, firstColumn, lastColumn));
}
private void excelCell(Row row, int column, Object value, CellStyle style) {
Cell cell = row.createCell(column);
if (value instanceof Number number) cell.setCellValue(number.doubleValue());
else cell.setCellValue(String.valueOf(value == null ? "" : value));
cell.setCellStyle(style);
}
private void writePdfRows(PdfPTable table, List<HourlyMisRecord> records, int firstHour,
int columnCount) {
String user = null;
int[] totals = new int[24];
int grandTotal = 0;
for (HourlyMisRecord record : records) {
if (!record.getDisplayName().equals(user)) {
if (user != null) pdfTotal(table, totals, grandTotal, firstHour);
user = record.getDisplayName();
totals = new int[24];
grandTotal = 0;
pdfCell(table, user, true, "E3E8ED", columnCount);
}
pdfCell(table, record.getReceivedDate().toString(), false, null, 1);
for (int hour = firstHour; hour < 24; hour++) {
int value = record.getHour(hour);
totals[hour] += value;
pdfCell(table, String.valueOf(value), false, null, 1);
}
grandTotal += record.getTotal();
pdfCell(table, String.valueOf(record.getTotal()), false, null, 1);
}
if (user != null) pdfTotal(table, totals, grandTotal, firstHour);
}
private void pdfTotal(PdfPTable table, int[] totals, int grandTotal, int firstHour) {
pdfCell(table, "Total", true, "DDE8F1", 1);
for (int hour = firstHour; hour < 24; hour++) pdfCell(table, String.valueOf(totals[hour]), true, "DDE8F1", 1);
pdfCell(table, String.valueOf(grandTotal), true, "DDE8F1", 1);
}
private void pdfCell(PdfPTable table, String value, boolean bold, String background, int colspan) {
PdfPCell cell = new PdfPCell(new Phrase(value == null ? "" : value, pdfFont(8, bold)));
cell.setColspan(colspan);
cell.setHorizontalAlignment(colspan > 1 ? Element.ALIGN_LEFT : Element.ALIGN_CENTER);
cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
cell.setPadding(5);
cell.setBorderColor(new BaseColor(159, 178, 195));
if (background != null) cell.setBackgroundColor(new BaseColor(
Integer.parseInt(background.substring(0, 2), 16),
Integer.parseInt(background.substring(2, 4), 16),
Integer.parseInt(background.substring(4, 6), 16)));
table.addCell(cell);
}
private Paragraph centered(Object value, int size, boolean bold) {
Paragraph paragraph = new Paragraph(String.valueOf(value == null ? "" : value), pdfFont(size, bold));
paragraph.setAlignment(Element.ALIGN_CENTER);
paragraph.setSpacingAfter(3);
return paragraph;
}
private Font pdfFont(int size, boolean bold) {
return FontFactory.getFont(FontFactory.HELVETICA, size,
bold ? Font.BOLD : Font.NORMAL, BaseColor.BLACK);
}
public byte[] csv(List<HourlyMisRecord> rows, Map<String, Object> parameters) {
try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
JRCsvExporter exporter = new JRCsvExporter();
exporter.setExporterInput(new SimpleExporterInput(fill(rows, parameters)));
exporter.setExporterOutput(new SimpleWriterExporterOutput(output));
exporter.exportReport();
return output.toByteArray();
} catch (Exception exception) {
throw new IllegalStateException("Unable to export Hourly MIS as CSV", exception);
}
}
private JasperPrint fill(List<HourlyMisRecord> rows, Map<String, Object> parameters)
throws JRException {
JasperPrint print = JasperFillManager.fillReport(report, new HashMap<>(parameters),
new JRBeanCollectionDataSource(rows, false));
compactLeadingEmptyHours(print, firstActiveHour(rows));
return print;
}
private int firstActiveHour(List<HourlyMisRecord> rows) {
if (rows.isEmpty()) return 0;
HourlyMisRecord first = rows.getFirst();
for (int hour = 0; hour < 24; hour++) {
if (first.getHour(hour) != 0) return hour;
}
return 0;
}
private void compactLeadingEmptyHours(JasperPrint print, int firstHour) {
int hiddenWidth = firstHour * HOUR_COLUMN_WIDTH;
int hiddenEnd = DATE_COLUMN_WIDTH + hiddenWidth;
int visibleTableWidth = REPORT_CONTENT_WIDTH - hiddenWidth;
double widthScale = (double) FULL_CONTENT_WIDTH / visibleTableWidth;
for (JRPrintPage page : print.getPages()) {
Iterator<JRPrintElement> elements = page.getElements().iterator();
while (elements.hasNext()) {
JRPrintElement element = elements.next();
if (element.getX() >= DATE_COLUMN_WIDTH && element.getX() < hiddenEnd) {
elements.remove();
} else if (element.getWidth() <= DATE_COLUMN_WIDTH
&& element.getX() < REPORT_CONTENT_WIDTH) {
int compactedX = element.getX() >= hiddenEnd
? element.getX() - hiddenWidth : element.getX();
element.setX((int) Math.round(compactedX * widthScale));
element.setWidth((int) Math.round(element.getWidth() * widthScale));
}
}
}
}
}

View File

@@ -0,0 +1,93 @@
package matrix.services.mis;
import com.cygnus.db.CygnusDbExecutor;
import com.cygnus.db.RowView;
import lib.models.HourlyMisRecord;
import matrix.nimble.mis.model.Filters;
import matrix.nimble.model.Session;
import org.springframework.stereotype.Service;
import java.sql.Date;
import java.time.LocalDate;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@Service
public class HourlyMisService {
public static final int QUERY_ID = 136;
private static final Set<String> SOURCES = Set.of("edp", "dedupe", "opr", "port", "bopr");
private final CygnusDbExecutor dbExecutor;
private final HourlyMisReportRenderer renderer;
private final ReportDefinitionService definitions;
public HourlyMisService(CygnusDbExecutor dbExecutor, HourlyMisReportRenderer renderer,
ReportDefinitionService definitions) {
this.dbExecutor = dbExecutor;
this.renderer = renderer;
this.definitions = definitions;
}
public String renderHtml(Filters filter, Session session) {
return renderer.html(records(filter, session), parameters(filter, session),
definitions.hourlyMis(filter.getTitle()));
}
public byte[] renderXlsx(Filters filter, Session session) {
return renderer.xlsx(records(filter, session), parameters(filter, session));
}
public byte[] renderPdf(Filters filter, Session session) {
return renderer.pdf(records(filter, session), parameters(filter, session));
}
public byte[] renderCsv(Filters filter, Session session) {
return renderer.csv(records(filter, session), parameters(filter, session));
}
private List<HourlyMisRecord> records(Filters filter, Session session) {
String source = source(filter.getMtype());
List<RowView> rows = dbExecutor.query(QUERY_ID, new Object[] {
source, filter.getFromdate(), filter.getTodate(), session.getCompanyID(),
session.getBranchID()
});
Map<String, HourlyMisRecord> records = new LinkedHashMap<>();
for (RowView row : rows) {
long userId = ((Number) row.get("user_id")).longValue();
LocalDate date = date(row.get("received_date"));
String name = String.valueOf(row.get("display_name"));
String key = userId + "|" + date;
HourlyMisRecord record = records.computeIfAbsent(key,
ignored -> new HourlyMisRecord(userId, name, date));
record.addCases(((Number) row.get("time_period")).intValue(),
((Number) row.get("case_count")).intValue());
}
return List.copyOf(records.values());
}
private Map<String, Object> parameters(Filters filter, Session session) {
return Map.of(
"REPORT_TITLE", filter.getTitle(),
"COMPANY_NAME", session.getCompanyName().toUpperCase(Locale.ROOT),
"BRANCH_NAME", session.getBranchName(),
"FROM_DATE", filter.getFromdate(),
"TO_DATE", filter.getTodate());
}
private String source(String value) {
String source = value == null ? "" : value.toLowerCase(Locale.ROOT);
if (!SOURCES.contains(source)) {
throw new IllegalArgumentException("Unsupported hourly MIS source");
}
return source;
}
private LocalDate date(Object value) {
if (value instanceof LocalDate localDate) return localDate;
if (value instanceof Date date) return date.toLocalDate();
return LocalDate.parse(String.valueOf(value));
}
}

View File

@@ -0,0 +1,55 @@
package matrix.services.mis;
import com.cygnus.db.CygnusDbExecutor;
import com.cygnus.db.RowView;
import lib.models.ReportColumnDefinition;
import lib.models.ReportDefinition;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ReportDefinitionService {
private static final String DEFINITION_QUERY = "report-definition";
private final CygnusDbExecutor dbExecutor;
public ReportDefinitionService(CygnusDbExecutor dbExecutor) {
this.dbExecutor = dbExecutor;
}
public ReportDefinition hourlyMis(String fallbackTitle) {
try {
List<RowView> rows = dbExecutor.query(DEFINITION_QUERY, new Object[] { "hourly-mis" });
if (rows.isEmpty()) return defaults(fallbackTitle);
RowView first = rows.getFirst();
List<ReportColumnDefinition> columns = new ArrayList<>(rows.size());
for (RowView row : rows) {
columns.add(new ReportColumnDefinition(
text(row, "column_key"), text(row, "column_label"),
number(row, "display_order"), text(row, "alignment"),
number(row, "column_width"), bool(row, "is_visible"),
bool(row, "is_totalled")));
}
return new ReportDefinition("hourly-mis", text(first, "report_title"),
bool(first, "hide_leading_zero_columns"), List.copyOf(columns));
} catch (RuntimeException unavailable) {
return defaults(fallbackTitle);
}
}
private ReportDefinition defaults(String title) {
List<ReportColumnDefinition> columns = new ArrayList<>(26);
columns.add(new ReportColumnDefinition("date", "Date", 0, "left", 110, true, false));
for (int hour = 0; hour < 24; hour++) {
columns.add(new ReportColumnDefinition("hour" + hour, String.valueOf(hour),
hour + 1, "center", 55, true, true));
}
columns.add(new ReportColumnDefinition("total", "Total", 25, "center", 70, true, true));
return new ReportDefinition("hourly-mis", title, true, List.copyOf(columns));
}
private String text(RowView row, String column) { return String.valueOf(row.get(column)); }
private int number(RowView row, String column) { return ((Number) row.get(column)).intValue(); }
private boolean bool(RowView row, String column) { return Boolean.TRUE.equals(row.get(column)); }
}

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<jasperReport xmlns="http://jasperreports.sourceforge.net/jasperreports" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd" name="hourly_mis" pageWidth="1800" pageHeight="1200" orientation="Landscape" columnWidth="1690" leftMargin="55" rightMargin="55" topMargin="30" bottomMargin="30" whenNoDataType="NoDataSection">
<property name="net.sf.jasperreports.export.xlsx.freeze.row" value="5"/>
<parameter name="REPORT_TITLE" class="java.lang.String"/>
<parameter name="COMPANY_NAME" class="java.lang.String"/>
<parameter name="BRANCH_NAME" class="java.lang.String"/>
<parameter name="FROM_DATE" class="java.lang.String"/>
<parameter name="TO_DATE" class="java.lang.String"/>
<field name="displayName" class="java.lang.String"/>
<field name="receivedDate" class="java.time.LocalDate"/>
<field name="hour00" class="java.lang.Integer"/>
<field name="hour01" class="java.lang.Integer"/>
<field name="hour02" class="java.lang.Integer"/>
<field name="hour03" class="java.lang.Integer"/>
<field name="hour04" class="java.lang.Integer"/>
<field name="hour05" class="java.lang.Integer"/>
<field name="hour06" class="java.lang.Integer"/>
<field name="hour07" class="java.lang.Integer"/>
<field name="hour08" class="java.lang.Integer"/>
<field name="hour09" class="java.lang.Integer"/>
<field name="hour10" class="java.lang.Integer"/>
<field name="hour11" class="java.lang.Integer"/>
<field name="hour12" class="java.lang.Integer"/>
<field name="hour13" class="java.lang.Integer"/>
<field name="hour14" class="java.lang.Integer"/>
<field name="hour15" class="java.lang.Integer"/>
<field name="hour16" class="java.lang.Integer"/>
<field name="hour17" class="java.lang.Integer"/>
<field name="hour18" class="java.lang.Integer"/>
<field name="hour19" class="java.lang.Integer"/>
<field name="hour20" class="java.lang.Integer"/>
<field name="hour21" class="java.lang.Integer"/>
<field name="hour22" class="java.lang.Integer"/>
<field name="hour23" class="java.lang.Integer"/>
<field name="total" class="java.lang.Integer"/>
<variable name="sum_hour00" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour00}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour01" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour01}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour02" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour02}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour03" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour03}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour04" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour04}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour05" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour05}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour06" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour06}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour07" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour07}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour08" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour08}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour09" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour09}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour10" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour10}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour11" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour11}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour12" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour12}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour13" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour13}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour14" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour14}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour15" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour15}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour16" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour16}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour17" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour17}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour18" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour18}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour19" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour19}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour20" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour20}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour21" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour21}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour22" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour22}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_hour23" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{hour23}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<variable name="sum_total" class="java.lang.Integer" resetType="Group" resetGroup="operatorGroup" calculation="Sum"><variableExpression><![CDATA[$F{total}]]></variableExpression><initialValueExpression><![CDATA[0]]></initialValueExpression></variable>
<group name="operatorGroup">
<groupExpression><![CDATA[$F{displayName}]]></groupExpression>
<groupHeader><band height="25"><textField><reportElement x="0" y="2" width="1690" height="22" backcolor="#E3E8ED" mode="Opaque"/><textElement verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$F{displayName}]]></textFieldExpression></textField></band></groupHeader>
<groupFooter><band height="24"><staticText><reportElement x="0" y="0" width="105" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[Total]]></text></staticText>
<textField><reportElement x="105" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour00}]]></textFieldExpression></textField>
<textField><reportElement x="160" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour01}]]></textFieldExpression></textField>
<textField><reportElement x="215" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour02}]]></textFieldExpression></textField>
<textField><reportElement x="270" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour03}]]></textFieldExpression></textField>
<textField><reportElement x="325" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour04}]]></textFieldExpression></textField>
<textField><reportElement x="380" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour05}]]></textFieldExpression></textField>
<textField><reportElement x="435" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour06}]]></textFieldExpression></textField>
<textField><reportElement x="490" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour07}]]></textFieldExpression></textField>
<textField><reportElement x="545" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour08}]]></textFieldExpression></textField>
<textField><reportElement x="600" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour09}]]></textFieldExpression></textField>
<textField><reportElement x="655" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour10}]]></textFieldExpression></textField>
<textField><reportElement x="710" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour11}]]></textFieldExpression></textField>
<textField><reportElement x="765" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour12}]]></textFieldExpression></textField>
<textField><reportElement x="820" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour13}]]></textFieldExpression></textField>
<textField><reportElement x="875" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour14}]]></textFieldExpression></textField>
<textField><reportElement x="930" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour15}]]></textFieldExpression></textField>
<textField><reportElement x="985" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour16}]]></textFieldExpression></textField>
<textField><reportElement x="1040" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour17}]]></textFieldExpression></textField>
<textField><reportElement x="1095" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour18}]]></textFieldExpression></textField>
<textField><reportElement x="1150" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour19}]]></textFieldExpression></textField>
<textField><reportElement x="1205" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour20}]]></textFieldExpression></textField>
<textField><reportElement x="1260" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour21}]]></textFieldExpression></textField>
<textField><reportElement x="1315" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour22}]]></textFieldExpression></textField>
<textField><reportElement x="1370" y="0" width="55" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_hour23}]]></textFieldExpression></textField>
<textField><reportElement x="1425" y="0" width="85" height="22" backcolor="#DDE8F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><textFieldExpression><![CDATA[$V{sum_total}]]></textFieldExpression></textField></band></groupFooter>
</group>
<title><band height="82">
<textField><reportElement x="0" y="0" width="1690" height="24"/><textElement textAlignment="Center"><font size="16" isBold="true"/></textElement><textFieldExpression><![CDATA[$P{COMPANY_NAME}]]></textFieldExpression></textField>
<textField><reportElement x="0" y="25" width="1690" height="20"/><textElement textAlignment="Center"><font size="12" isBold="true"/></textElement><textFieldExpression><![CDATA[$P{REPORT_TITLE}]]></textFieldExpression></textField>
<textField><reportElement x="0" y="46" width="1690" height="18"/><textElement textAlignment="Center"><font size="11"/></textElement><textFieldExpression><![CDATA["From: " + $P{FROM_DATE} + " To: " + $P{TO_DATE}]]></textFieldExpression></textField>
<textField><reportElement x="0" y="64" width="1690" height="18"/><textElement><font size="11" isBold="true"/></textElement><textFieldExpression><![CDATA["Branch: " + $P{BRANCH_NAME}]]></textFieldExpression></textField>
</band></title>
<columnHeader><band height="24"><staticText><reportElement x="0" y="0" width="105" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[Date]]></text></staticText>
<staticText><reportElement x="105" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[0]]></text></staticText>
<staticText><reportElement x="160" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[1]]></text></staticText>
<staticText><reportElement x="215" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[2]]></text></staticText>
<staticText><reportElement x="270" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[3]]></text></staticText>
<staticText><reportElement x="325" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[4]]></text></staticText>
<staticText><reportElement x="380" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[5]]></text></staticText>
<staticText><reportElement x="435" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[6]]></text></staticText>
<staticText><reportElement x="490" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[7]]></text></staticText>
<staticText><reportElement x="545" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[8]]></text></staticText>
<staticText><reportElement x="600" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[9]]></text></staticText>
<staticText><reportElement x="655" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[10]]></text></staticText>
<staticText><reportElement x="710" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[11]]></text></staticText>
<staticText><reportElement x="765" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[12]]></text></staticText>
<staticText><reportElement x="820" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[13]]></text></staticText>
<staticText><reportElement x="875" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[14]]></text></staticText>
<staticText><reportElement x="930" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[15]]></text></staticText>
<staticText><reportElement x="985" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[16]]></text></staticText>
<staticText><reportElement x="1040" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[17]]></text></staticText>
<staticText><reportElement x="1095" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[18]]></text></staticText>
<staticText><reportElement x="1150" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[19]]></text></staticText>
<staticText><reportElement x="1205" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[20]]></text></staticText>
<staticText><reportElement x="1260" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[21]]></text></staticText>
<staticText><reportElement x="1315" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[22]]></text></staticText>
<staticText><reportElement x="1370" y="0" width="55" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[23]]></text></staticText>
<staticText><reportElement x="1425" y="0" width="85" height="24" backcolor="#DCE7F1" mode="Opaque"/><box><pen lineWidth="0.5" lineColor="#9FB2C3"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10" isBold="true"/></textElement><text><![CDATA[Total]]></text></staticText></band></columnHeader>
<detail><band height="22"><textField><reportElement x="0" y="0" width="105" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Left" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{receivedDate}]]></textFieldExpression></textField>
<textField><reportElement x="105" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour00}]]></textFieldExpression></textField>
<textField><reportElement x="160" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour01}]]></textFieldExpression></textField>
<textField><reportElement x="215" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour02}]]></textFieldExpression></textField>
<textField><reportElement x="270" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour03}]]></textFieldExpression></textField>
<textField><reportElement x="325" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour04}]]></textFieldExpression></textField>
<textField><reportElement x="380" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour05}]]></textFieldExpression></textField>
<textField><reportElement x="435" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour06}]]></textFieldExpression></textField>
<textField><reportElement x="490" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour07}]]></textFieldExpression></textField>
<textField><reportElement x="545" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour08}]]></textFieldExpression></textField>
<textField><reportElement x="600" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour09}]]></textFieldExpression></textField>
<textField><reportElement x="655" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour10}]]></textFieldExpression></textField>
<textField><reportElement x="710" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour11}]]></textFieldExpression></textField>
<textField><reportElement x="765" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour12}]]></textFieldExpression></textField>
<textField><reportElement x="820" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour13}]]></textFieldExpression></textField>
<textField><reportElement x="875" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour14}]]></textFieldExpression></textField>
<textField><reportElement x="930" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour15}]]></textFieldExpression></textField>
<textField><reportElement x="985" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour16}]]></textFieldExpression></textField>
<textField><reportElement x="1040" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour17}]]></textFieldExpression></textField>
<textField><reportElement x="1095" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour18}]]></textFieldExpression></textField>
<textField><reportElement x="1150" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour19}]]></textFieldExpression></textField>
<textField><reportElement x="1205" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour20}]]></textFieldExpression></textField>
<textField><reportElement x="1260" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour21}]]></textFieldExpression></textField>
<textField><reportElement x="1315" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour22}]]></textFieldExpression></textField>
<textField><reportElement x="1370" y="0" width="55" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{hour23}]]></textFieldExpression></textField>
<textField><reportElement x="1425" y="0" width="85" height="22"/><box><pen lineWidth="0.35" lineColor="#B9C8D5"/></box><textElement textAlignment="Center" verticalAlignment="Middle"><font size="10"/></textElement><textFieldExpression><![CDATA[$F{total}]]></textFieldExpression></textField></band></detail>
<noData><band height="80"><staticText><reportElement x="0" y="25" width="1690" height="30"/><textElement textAlignment="Center"><font size="13" isBold="true" isItalic="true"/></textElement><text><![CDATA[No Records Found]]></text></staticText></band></noData>
</jasperReport>

View File

@@ -9,6 +9,9 @@ public interface CygnusDbExecutor {
<T> List<T> query(int queryId, Object[] parameters, Class<T> responseType);
<T> List<T> query(int queryId, Object[] parameters, RowMapper<T> mapper);
List<RowView> query(int queryId, Object[] parameters);
default List<RowView> query(String queryKey, Object[] parameters) {
throw new UnsupportedOperationException("Query keys are not supported by this executor");
}
<T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> responseType);
int insert(int queryId, Object[] parameters);
<K> K insert(int queryId, Object[] parameters, Class<K> generatedKeyType);

View File

@@ -63,6 +63,15 @@ public final class JdbcCygnusDbExecutor implements CygnusDbExecutor {
return query(queryId, parameters, RowView.class);
}
@Override
public List<RowView> query(String queryKey, Object[] parameters) {
QueryDefinition definition = queryProvider.get(queryKey);
if (definition.type() != QueryType.SELECT) throw new QueryDefinitionException(
"Query " + queryKey + " is " + definition.type() + ", not " + QueryType.SELECT);
return withReadConnection(definition.queryId(), connection -> query(
connection, definition, parameters, RowView.class));
}
@Override
public <T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> responseType) {
List<T> rows = query(queryId, parameters, responseType);

View File

@@ -0,0 +1,6 @@
-- Structured Hourly MIS dataset used by JasperReports. The legacy hourlymis()
-- function is retained temporarily for rollback, but query 136 no longer calls it.
UPDATE platform.application_query
SET query_text = 'select!C0L!WITH args AS (SELECT ?::text AS source, ?::timestamp AS from_date, ?::timestamp AS to_date, ?::integer AS company_id, ?::integer AS branch_id), hourly_data AS (SELECT m.addedby::bigint AS user_id, COALESCE(NULLIF(m.addedbyuser, ''''), u.displayname) AS display_name, m.receivedate::date AS received_date, EXTRACT(HOUR FROM m.addedon)::integer AS time_period FROM main m CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=m.addedby WHERE a.source=''edp'' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.addedon BETWEEN a.from_date AND a.to_date UNION ALL SELECT d.deduperunby::bigint, u.displayname, m.receivedate::date, EXTRACT(HOUR FROM d.deduperunon)::integer FROM main_mainopr m JOIN dedupe d ON d.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=d.deduperunby WHERE a.source=''dedupe'' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.isdeleted=0 AND d.deduperunon BETWEEN a.from_date AND a.to_date UNION ALL SELECT r.opruser::bigint, u.displayname, r.oprtime::date, EXTRACT(HOUR FROM r.oprtime)::integer FROM main_mainopr m JOIN riskalerts r ON r.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=r.opruser WHERE a.source=''opr'' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND r.oprtime BETWEEN a.from_date AND a.to_date UNION ALL SELECT p.portfolio_id::bigint, p.portname, m.sdoneon::date, EXTRACT(HOUR FROM m.sdoneon)::integer FROM main_mainopr m JOIN portfolio p ON p.portfolio_id=m.portfolio_id CROSS JOIN args a WHERE a.source=''port'' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.sdone=1 AND m.sdoneon BETWEEN a.from_date AND a.to_date UNION ALL SELECT CASE r.visit WHEN ''RESI'' THEN m.rbksoftupdtdby WHEN ''OFFICE'' THEN m.obksoftupdtdby ELSE m.pbksoftupdtdby END::bigint, u.displayname, CASE r.visit WHEN ''RESI'' THEN m.rbksoftupdtdon WHEN ''OFFICE'' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END::date, EXTRACT(HOUR FROM CASE r.visit WHEN ''RESI'' THEN m.rbksoftupdtdon WHEN ''OFFICE'' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END)::integer FROM main_mainopr m JOIN riskalerts r ON r.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=CASE r.visit WHEN ''RESI'' THEN m.rbksoftupdtdby WHEN ''OFFICE'' THEN m.obksoftupdtdby ELSE m.pbksoftupdtdby END WHERE a.source=''bopr'' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND CASE r.visit WHEN ''RESI'' THEN m.rbksoftupdtdon WHEN ''OFFICE'' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END BETWEEN a.from_date AND a.to_date) SELECT user_id, COALESCE(display_name, ''Unknown'') AS display_name, received_date, time_period, COUNT(*)::integer AS case_count FROM hourly_data GROUP BY user_id, display_name, received_date, time_period ORDER BY display_name, received_date, time_period',
ismigrated = true
WHERE query_id = 136;

View File

@@ -0,0 +1,44 @@
CREATE TABLE IF NOT EXISTS platform.report_definition (
report_key varchar(100) PRIMARY KEY,
report_title varchar(200) NOT NULL,
hide_leading_zero_columns boolean NOT NULL DEFAULT false,
enabled boolean NOT NULL DEFAULT true,
updated_at timestamptz NOT NULL DEFAULT current_timestamp
);
CREATE TABLE IF NOT EXISTS platform.report_column_definition (
report_key varchar(100) NOT NULL REFERENCES platform.report_definition(report_key) ON DELETE CASCADE,
column_key varchar(100) NOT NULL,
column_label varchar(100) NOT NULL,
display_order integer NOT NULL,
alignment varchar(10) NOT NULL DEFAULT 'left',
column_width integer NOT NULL DEFAULT 80,
is_visible boolean NOT NULL DEFAULT true,
is_totalled boolean NOT NULL DEFAULT false,
PRIMARY KEY (report_key, column_key)
);
INSERT INTO platform.report_definition(report_key, report_title, hide_leading_zero_columns)
VALUES ('hourly-mis', 'HOURLY MIS (EDP)', true)
ON CONFLICT (report_key) DO NOTHING;
INSERT INTO platform.report_column_definition
(report_key,column_key,column_label,display_order,alignment,column_width,is_visible,is_totalled)
VALUES ('hourly-mis','date','Date',0,'left',110,true,false),
('hourly-mis','total','Total',25,'center',70,true,true)
ON CONFLICT (report_key,column_key) DO NOTHING;
INSERT INTO platform.report_column_definition
(report_key,column_key,column_label,display_order,alignment,column_width,is_visible,is_totalled)
SELECT 'hourly-mis', 'hour'||hour, hour::text, hour+1, 'center', 55, true, true
FROM generate_series(0,23) hour
ON CONFLICT (report_key,column_key) DO NOTHING;
UPDATE platform.application_query
SET query_text='select!C0L!SELECT r.report_title,r.hide_leading_zero_columns,c.column_key,c.column_label,c.display_order,c.alignment,c.column_width,c.is_visible,c.is_totalled FROM platform.report_definition r JOIN platform.report_column_definition c ON c.report_key=r.report_key WHERE r.report_key=? AND r.enabled=true ORDER BY c.display_order',
enabled=true, updated_at=current_timestamp
WHERE query_key='report-definition';
INSERT INTO platform.application_query(query_key, query_text, enabled)
SELECT 'report-definition', 'select!C0L!SELECT r.report_title,r.hide_leading_zero_columns,c.column_key,c.column_label,c.display_order,c.alignment,c.column_width,c.is_visible,c.is_totalled FROM platform.report_definition r JOIN platform.report_column_definition c ON c.report_key=r.report_key WHERE r.report_key=? AND r.enabled=true ORDER BY c.display_order', true
WHERE NOT EXISTS (SELECT 1 FROM platform.application_query WHERE query_key='report-definition');

View File

@@ -136,7 +136,7 @@ Query132!C0L!select!C0L!select d.uuid,d.mvcode,d.applno,d.customername,d.apptype
Query133!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as receivedate,applno,upper(customername) cname,bank_branch_id,'DDUP Report' as label1,name as portname,to_char(receivedate,'dd/MM/yyyy') as receivedate,applno,customername,'' as blankcol1,'' as blank1,(case when earlier='Y' then earremarks else 'NO EARLIER MATCH FOUND' end) as earremarks,'' as blank2,(case when negative='Y' then negremarks else 'NO NEGATIVE MATCH FOUND' end) as negremarks,(case when negative='Y' then 'STATUS: NEGATIVE' else '' end) as label3,'' as blankcol2,'End of Report' as label4,to_char(deduperunon,'HH24:MI:SS') as dedtime,'Supervisor Name' as label5,'Agency Seal' as label6,'QR Code' as label7,portsupervisor,'images/signs/'||supervisor||'.png' as seal,mvcode||'-'||applno||'-'||customername||'-EAR-'||earlier||'-NEG-'||negative as bcode from banklist_main_dedupe d where portfolio_id not in (select portfolio_id from denied_process where process_id=13 and isdenied=1) and (val0) val1
Query134!C0L!select!C0L!select portfolio_id,portname from portfolio where portfolio_id not in (select portfolio_id from denied_process where process_id=13 and isdenied=1) and branch_id=val0
Query135!C0L!insert!C0L!insert into sys_event_log (msgtype,code,message,details,user_id,occuredon,ipaddr,branch_id) values ('val3','val2','val4','val5',val0,'val6','127.0.0.1',val1)
Query136!C0L!procedure!C0L!select hourlymis('val0','val1',val2,'val3','val4','val5','val6')
Query136!C0L!select!C0L!WITH args AS (SELECT ?::text AS source, ?::timestamp AS from_date, ?::timestamp AS to_date, ?::integer AS company_id, ?::integer AS branch_id), hourly_data AS (SELECT m.addedby::bigint AS user_id, COALESCE(NULLIF(m.addedbyuser, ''), u.displayname) AS display_name, m.receivedate::date AS received_date, EXTRACT(HOUR FROM m.addedon)::integer AS time_period FROM main m CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=m.addedby WHERE a.source='edp' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.addedon BETWEEN a.from_date AND a.to_date UNION ALL SELECT d.deduperunby::bigint, u.displayname, m.receivedate::date, EXTRACT(HOUR FROM d.deduperunon)::integer FROM main_mainopr m JOIN dedupe d ON d.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=d.deduperunby WHERE a.source='dedupe' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.isdeleted=0 AND d.deduperunon BETWEEN a.from_date AND a.to_date UNION ALL SELECT r.opruser::bigint, u.displayname, r.oprtime::date, EXTRACT(HOUR FROM r.oprtime)::integer FROM main_mainopr m JOIN riskalerts r ON r.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=r.opruser WHERE a.source='opr' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND r.oprtime BETWEEN a.from_date AND a.to_date UNION ALL SELECT p.portfolio_id::bigint, p.portname, m.sdoneon::date, EXTRACT(HOUR FROM m.sdoneon)::integer FROM main_mainopr m JOIN portfolio p ON p.portfolio_id=m.portfolio_id CROSS JOIN args a WHERE a.source='port' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND m.sdone=1 AND m.sdoneon BETWEEN a.from_date AND a.to_date UNION ALL SELECT CASE r.visit WHEN 'RESI' THEN m.rbksoftupdtdby WHEN 'OFFICE' THEN m.obksoftupdtdby ELSE m.pbksoftupdtdby END::bigint, u.displayname, CASE r.visit WHEN 'RESI' THEN m.rbksoftupdtdon WHEN 'OFFICE' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END::date, EXTRACT(HOUR FROM CASE r.visit WHEN 'RESI' THEN m.rbksoftupdtdon WHEN 'OFFICE' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END)::integer FROM main_mainopr m JOIN riskalerts r ON r.uuid=m.uuid CROSS JOIN args a LEFT JOIN app_user u ON u.user_id=CASE r.visit WHEN 'RESI' THEN m.rbksoftupdtdby WHEN 'OFFICE' THEN m.obksoftupdtdby ELSE m.pbksoftupdtdby END WHERE a.source='bopr' AND m.company_id=a.company_id AND m.branch_id=a.branch_id AND CASE r.visit WHEN 'RESI' THEN m.rbksoftupdtdon WHEN 'OFFICE' THEN m.obksoftupdtdon ELSE m.pbksoftupdtdon END BETWEEN a.from_date AND a.to_date) SELECT user_id, COALESCE(display_name, 'Unknown') AS display_name, received_date, time_period, COUNT(*)::integer AS case_count FROM hourly_data GROUP BY user_id, display_name, received_date, time_period ORDER BY display_name, received_date, time_period
Query137!C0L!procedure!C0L!select receivedcases('val0', 'val1', val2, val3, val4, 'val5', 'val6', 'val7', 'val8')
Query138!C0L!procedure!C0L!select reportpending(val0, val1, val2, 'val3', 'val4', 'val5', 'val6')
Query139!C0L!procedure!C0L!select pendingcases(val0, val1, val2, 'val3', 'val4', 'val5', 'val6')

View File

@@ -40,5 +40,6 @@
<reactor.netty.version>1.2.8</reactor.netty.version>
<nimbus.version>10.4</nimbus.version>
<lombok.version>1.18.46</lombok.version>
<jasperreports.version>6.21.5</jasperreports.version>
</properties>
</project>