4 Commits

81 changed files with 3123 additions and 662 deletions

2
.gitignore vendored
View File

@@ -30,7 +30,7 @@
/.nb-gradle/
/cygnus-onprem-app/target
/cygnus-cloud-client/target
/cygnus-installer/src/test
/cygnus-cloud-service/target
/cygnus-installer/src/target
/cygnus-installer/target
/cygnus-onprem-db/target

47
cygnus-lib/pom.xml Normal file
View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cygnus</groupId>
<artifactId>cygnus-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cygnus-lib</artifactId>
<packaging>jar</packaging>
<name>Cygnus Shared Library</name>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,71 @@
package lib.constants;
import java.util.Arrays;
/** Canonical user-facing errors shared by Cygnus applications. */
public enum ApplicationError {
BAD_REQUEST(400, "HTTP-400", "Invalid request",
"Cygnus could not process the submitted request.",
"Review the supplied information and try again."),
SESSION_REQUIRED(401, "AUTH-401", "Sign in required",
"Your session is missing or has expired.",
"Sign in again to continue securely."),
ACCESS_DENIED(403, "AUTH-403", "Access denied",
"You do not have permission to open this page.",
"Contact your administrator if this feature should be available to you."),
PAGE_NOT_FOUND(404, "HTTP-404", "Page not found",
"The requested page could not be found.",
"Check the address or return to the application dashboard."),
METHOD_NOT_ALLOWED(405, "HTTP-405", "Action not allowed",
"This page does not support the requested action.",
"Return to the previous page and try an available action."),
CONFLICT(409, "HTTP-409", "Request conflict",
"The request conflicts with the current state of the data.",
"Refresh the page and try again."),
PAYLOAD_TOO_LARGE(413, "HTTP-413", "File is too large",
"The submitted content exceeds the permitted size.",
"Reduce the file size and try again."),
TOO_MANY_REQUESTS(429, "HTTP-429", "Too many requests",
"Cygnus has received too many requests in a short period.",
"Wait briefly before trying again."),
INTERNAL_SERVER_ERROR(500, "HTTP-500", "Something went wrong",
"Cygnus could not complete your request.",
"Try again. If the issue continues, share the reference ID with support."),
BAD_GATEWAY(502, "HTTP-502", "Service response unavailable",
"A required service returned an invalid response.",
"Try again shortly. If the issue continues, contact support."),
SERVICE_UNAVAILABLE(503, "HTTP-503", "Service temporarily unavailable",
"A required Cygnus service is currently unavailable.",
"Wait briefly and try again."),
GATEWAY_TIMEOUT(504, "HTTP-504", "Service response timed out",
"A required service took too long to respond.",
"Try the request again shortly.");
private final int httpStatus;
private final String code;
private final String title;
private final String message;
private final String description;
ApplicationError(
int httpStatus, String code, String title, String message, String description) {
this.httpStatus = httpStatus;
this.code = code;
this.title = title;
this.message = message;
this.description = description;
}
public static ApplicationError fromHttpStatus(int status) {
return Arrays.stream(values())
.filter(error -> error.httpStatus == status)
.findFirst()
.orElse(INTERNAL_SERVER_ERROR);
}
public int getHttpStatus() { return httpStatus; }
public String getCode() { return code; }
public String getTitle() { return title; }
public String getMessage() { return message; }
public String getDescription() { return description; }
}

View File

@@ -0,0 +1,23 @@
package lib.models;
import java.util.List;
import java.util.Map;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class CasePunching {
private ErrorDetails errorDetails;
private Map<String, String> optionsl;
private Map<String, List<Option>> options;
private List<String> visibleSections;
private Integer portfolioId;
private Integer formMode;
private String userId;
private String dynamicFields;
private String dynamicHtml;
private String verificationCaseId;
private String documentCaseId;
}

View File

@@ -0,0 +1,58 @@
package lib.models;
import lib.constants.ApplicationError;
import java.io.Serial;
import java.io.Serializable;
import java.time.Instant;
import java.util.Objects;
import java.util.UUID;
import lombok.Getter;
/** Dependency-free error information shared across Cygnus modules. */
@Getter
public final class ErrorDetails implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final int httpStatus;
private final String errorCode;
private final String title;
private final String message;
private final String description;
private final String referenceId;
private final Instant timestamp;
public ErrorDetails(ApplicationError error) {
this(error.getHttpStatus(), error.getCode(), error.getTitle(),
error.getMessage(), error.getDescription(),
UUID.randomUUID().toString(), Instant.now());
}
private ErrorDetails(
int httpStatus,
String errorCode,
String title,
String message,
String description,
String referenceId,
Instant timestamp) {
if (httpStatus < 400 || httpStatus > 599) {
throw new IllegalArgumentException("HTTP error status must be between 400 and 599");
}
this.httpStatus = httpStatus;
this.errorCode = required(errorCode, "errorCode");
this.title = required(title, "title");
this.message = required(message, "message");
this.description = description == null ? "" : description.trim();
this.referenceId = required(referenceId, "referenceId");
this.timestamp = Objects.requireNonNull(timestamp, "timestamp");
}
private static String required(String value, String name) {
if (value == null || value.isBlank()) {
throw new IllegalArgumentException(name + " is required");
}
return value.trim();
}
}

View File

@@ -0,0 +1,13 @@
package lib.models;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Option {
private Object value;
private String label;
private String description;
private String group;
}

View File

@@ -0,0 +1,22 @@
package lib.models;
import lib.constants.ApplicationError;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class ErrorDetailsTest {
@Test
void createsAReusableErrorContract() {
ErrorDetails details = new ErrorDetails(ApplicationError.ACCESS_DENIED);
assertEquals(403, details.getHttpStatus());
assertEquals("AUTH-403", details.getErrorCode());
}
@Test
void mapsHttpStatusToCanonicalError() {
assertEquals(ApplicationError.PAGE_NOT_FOUND, ApplicationError.fromHttpStatus(404));
assertEquals(ApplicationError.INTERNAL_SERVER_ERROR,
ApplicationError.fromHttpStatus(599));
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,3 @@
artifactId=cygnus-lib
groupId=com.cygnus
version=1.0.0-SNAPSHOT

View File

@@ -0,0 +1,3 @@
lib/models/ErrorDetails.class
lib/models/CasePunching.class
lib/constants/ApplicationError.class

View File

@@ -0,0 +1,4 @@
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/constants/ApplicationError.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/CasePunching.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/ErrorDetails.java
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/main/java/lib/models/Option.java

View File

@@ -0,0 +1 @@
lib/models/ErrorDetailsTest.class

View File

@@ -0,0 +1 @@
/Users/maddy/Projects/cygnus/matrix/cygnus-lib/src/test/java/lib/models/ErrorDetailsTest.java

View File

@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuite xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://maven.apache.org/surefire/maven-surefire-plugin/xsd/surefire-test-report.xsd" version="3.0.2" name="lib.models.ErrorDetailsTest" time="0.024" tests="2" errors="0" skipped="0" failures="0">
<properties>
<property name="java.specification.version" value="25"/>
<property name="sun.jnu.encoding" value="UTF-8"/>
<property name="java.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/test-classes:/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/classes:/Users/maddy/.m2/repository/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
<property name="java.vm.vendor" value="Homebrew"/>
<property name="sun.arch.data.model" value="64"/>
<property name="java.vendor.url" value="https://github.com/Homebrew/homebrew-core/issues"/>
<property name="os.name" value="Mac OS X"/>
<property name="java.vm.specification.version" value="25"/>
<property name="sun.java.launcher" value="SUN_STANDARD"/>
<property name="user.country" value="US"/>
<property name="sun.boot.library.path" value="/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home/lib"/>
<property name="sun.java.command" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801204253825_3.jar /Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire 2026-08-01T20-42-53_787-jvmRun1 surefire-20260801204253825_1tmp surefire_0-20260801204253825_2tmp"/>
<property name="http.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="jdk.debug" value="release"/>
<property name="surefire.test.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/test-classes:/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/classes:/Users/maddy/.m2/repository/org/projectlombok/lombok/1.18.46/lombok-1.18.46.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter/5.12.2/junit-jupiter-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.12.2/junit-jupiter-api-5.12.2.jar:/Users/maddy/.m2/repository/org/opentest4j/opentest4j/1.3.0/opentest4j-1.3.0.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-commons/1.12.2/junit-platform-commons-1.12.2.jar:/Users/maddy/.m2/repository/org/apiguardian/apiguardian-api/1.1.2/apiguardian-api-1.1.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.12.2/junit-jupiter-params-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.12.2/junit-jupiter-engine-5.12.2.jar:/Users/maddy/.m2/repository/org/junit/platform/junit-platform-engine/1.12.2/junit-platform-engine-1.12.2.jar:"/>
<property name="sun.cpu.endian" value="little"/>
<property name="user.home" value="/Users/maddy"/>
<property name="user.language" value="en"/>
<property name="java.specification.vendor" value="Oracle Corporation"/>
<property name="java.version.date" value="2026-01-20"/>
<property name="java.home" value="/opt/homebrew/Cellar/openjdk/25.0.2/libexec/openjdk.jdk/Contents/Home"/>
<property name="file.separator" value="/"/>
<property name="basedir" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib"/>
<property name="java.vm.compressedOopsMode" value="Zero based"/>
<property name="line.separator" value="&#10;"/>
<property name="java.vm.specification.vendor" value="Oracle Corporation"/>
<property name="java.specification.name" value="Java Platform API Specification"/>
<property name="apple.awt.application.name" value="ForkedBooter"/>
<property name="surefire.real.class.path" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib/target/surefire/surefirebooter-20260801204253825_3.jar"/>
<property name="sun.management.compiler" value="HotSpot 64-Bit Tiered Compilers"/>
<property name="ftp.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.runtime.version" value="25.0.2"/>
<property name="user.name" value="maddy"/>
<property name="stdout.encoding" value="UTF-8"/>
<property name="path.separator" value=":"/>
<property name="os.version" value="26.5.2"/>
<property name="java.runtime.name" value="OpenJDK Runtime Environment"/>
<property name="file.encoding" value="UTF-8"/>
<property name="java.vm.name" value="OpenJDK 64-Bit Server VM"/>
<property name="java.vendor.version" value="Homebrew"/>
<property name="localRepository" value="/Users/maddy/.m2/repository"/>
<property name="java.vendor.url.bug" value="https://github.com/Homebrew/homebrew-core/issues"/>
<property name="java.io.tmpdir" value="/var/folders/1l/36214rdn79755j30lcnmgsqh0000gn/T/"/>
<property name="java.version" value="25.0.2"/>
<property name="user.dir" value="/Users/maddy/Projects/cygnus/matrix/cygnus-lib"/>
<property name="os.arch" value="aarch64"/>
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
<property name="native.encoding" value="UTF-8"/>
<property name="java.library.path" value="/Users/maddy/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:."/>
<property name="java.vm.info" value="mixed mode, sharing"/>
<property name="stderr.encoding" value="UTF-8"/>
<property name="java.vendor" value="Homebrew"/>
<property name="java.vm.version" value="25.0.2"/>
<property name="stdin.encoding" value="UTF-8"/>
<property name="sun.io.unicode.encoding" value="UnicodeBig"/>
<property name="socksNonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="java.class.version" value="69.0"/>
</properties>
<testcase name="mapsHttpStatusToCanonicalError" classname="lib.models.ErrorDetailsTest" time="0.008"/>
<testcase name="createsAReusableErrorContract" classname="lib.models.ErrorDetailsTest" time="0.007"/>
</testsuite>

View File

@@ -0,0 +1,4 @@
-------------------------------------------------------------------------------
Test set: lib.models.ErrorDetailsTest
-------------------------------------------------------------------------------
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.024 s -- in lib.models.ErrorDetailsTest

View File

@@ -10,7 +10,7 @@
<mvc:annotation-driven/>
<!-- Scan for annotation based controllers -->
<context:component-scan base-package="matrix.nimble" />
<context:component-scan base-package="matrix.nimble,matrix.services" />
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/app/" />

View File

@@ -33,7 +33,7 @@
<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/punch-workspace-v3.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.css?v=5" rel="stylesheet" type="text/css" />
<script src="/matrix/js/matrix-dialog-child.js" type="text/javascript"></script>
<script src="/matrix/js/bootstrap-5.3.8.bundle.min.js" defer></script>
<script src="/matrix/js/matrix-shell-v2.js" defer></script>
@@ -591,7 +591,7 @@
<div id="section6">
<!-- Title -->
<div class="title">
<img class="matrix-title-icon" src="/matrix/images/othdet.png" />
<img class="matrix-title-icon" src="/matrix/images/edit_form.png" alt="" />
<span class="matrix-title-text">Other Details</span>
</div>
<!-- -->

View File

@@ -3,6 +3,12 @@
<%@page language="java" session="true" %>
<%@taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<c:set var="pageFormMode" value="${not empty model ? model.formMode : formmode}" />
<c:set var="pagePortfolioId" value="${not empty model ? model.portfolioId : portfolio_id}" />
<c:set var="pageDynamicFields" value="${not empty model ? model.dynamicFields : dynamicfields}" />
<c:set var="pageDocumentCaseId" value="${not empty model ? model.documentCaseId : duuid}" />
<c:set var="pageUserId" value="${not empty model ? model.userId : usid}" />
<!DOCTYPE html>
<html>
<head>
@@ -14,8 +20,8 @@
<script language="javascript" src="/matrix/js/lib/jsfuncs.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ui/msgdialog.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ui/select.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/appjs/punching.js?ver=4" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/validator.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/appjs/punching.js?ver=6" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/validator.js?ver=2" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ajax.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ajax-dynamic-list.js?ver=1" type="text/javascript"></script>
@@ -25,55 +31,64 @@
<link href="/matrix/css/select.css" rel="stylesheet" type="text/css" />
<link href="/matrix/css/table.css" rel="stylesheet" type="text/css" />
<link href="/matrix/css/autocomplete.css" rel="stylesheet" type="text/css" />
<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/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/punch-workspace-v3.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.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>
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
<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/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/punch-workspace-v3.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.css?v=5" 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>
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
</head>
<body class="matrix-v2 matrix-shell matrix-tool-workspace matrix-punch-workspace matrix-add-cases">
<body class="matrix-v2 matrix-shell matrix-tool-workspace matrix-punch-workspace matrix-add-cases">
<div id='PageFrame'>
<!-- Title Bar -->
<%@ include file="/WEB-INF/app/fragments/app-title.jspf" %>
<%@ include file="/WEB-INF/app/fragments/app-title.jspf" %>
<!-- -->
<c:if test="${formmode!=2}">
<!-- Dynamic Menu -->
<nav class="matrix-shell__navigation" aria-label="Primary navigation">
<c:out value="${Sessvals.menuHtml}" escapeXml="false" />
</nav>
<c:if test="${pageFormMode!=2}">
<!-- Dynamic Menu -->
<nav class="matrix-shell__navigation" aria-label="Primary navigation">
<c:out value="${Sessvals.menuHtml}" escapeXml="false" />
</nav>
<!-- -->
</c:if>
<div id="formcontainer" class="matrix-tool-workspace__content">
<div id="formcontainer" class="matrix-tool-workspace__content">
<form method="post" name="casedetails" id="casedetails" >
<!-- Common Details (Section1) Visible for all portfolios-->
<div>
<!-- Title -->
<div class="title matrix-form-title">
<img class="matrix-title-icon" src="/matrix/images/commondet.png" align="absmiddle" />
<span class="matrix-title-text matrix-form-title__heading">Common Details <span id='recfound'></span></span>
<label class="matrix-form-title__field">Skip To <input type="text" class="textbox" style="width:30px" onkeypress="return NumberOnly(event)" onblur="GotoRecord(this,'recno')"></label>
<label class="matrix-form-title__field">Search By File No <input type="text" class="textbox" style="width:100px" onblur="GotoRecord(this,'query')"></label>
<div class="title matrix-form-title">
<img class="matrix-title-icon" src="/matrix/images/commondet.png" align="absmiddle" />
<span class="matrix-title-text matrix-form-title__heading">Common Details <span id='recfound'></span></span>
<label class="matrix-form-title__field">Skip To <input type="text" class="textbox" style="width:50px" onkeypress="return NumberOnly(event)" onblur="GotoRecord(this,'recno')"></label>
<label class="matrix-form-title__field">Search By File No <input type="text" class="textbox" style="width:150px" onblur="GotoRecord(this,'query')"></label>
<!-- Checkbox for AUTO CUT diabled for now -->
<label class="matrix-form-title__check"><input type="checkbox" name="autocut" checked id="autocut" /> Auto Cut-Off</label>
<c:if test="${formmode==2}">
<input type="hidden" id="portfolio_id" name="portfolio_id" value="-1" />
<label class="matrix-form-title__check"><input type="checkbox" name="autocut" checked id="autocut" /> Auto Cut-Off</label>
<c:if test="${pageFormMode==2}">
<input type="hidden" id="portfolio_id" name="portfolioId" value="${pagePortfolioId}" />
</c:if>
<c:if test="${formmode!=2}">
<div class="divselect matrix-form-title__end" id="divportfolio" style="width:217px;">
<select id="portfolio_id" name="portfolio_id" style="width:235px" onchange="SubmitForm('caseadd','_parent','casedetails')">
<c:if test="${pageFormMode!=2}">
<div class="divselect matrix-form-title__end" id="divportfolio" style="width:297px;">
<select id="portfolio_id" name="portfolioId" style="width:315px" onchange="SubmitForm('caseadd','_parent','casedetails')">
<option value="-1" selected >SELECT PORTFOLIO</option>
<c:forEach items="${portlist}" var="port">
<option value="${port[0]}">${port[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}">
<c:forEach items="${model.options['portfolio.']}" var="port">
<option value="${port.value}"><c:out value="${port.label}" /></option>
</c:forEach>
</c:when>
<c:otherwise>
<c:forEach items="${portlist}" var="port">
<option value="${port[0]}"><c:out value="${port[1]}" /></option>
</c:forEach>
</c:otherwise>
</c:choose>
</select>
</div>
</c:if>
@@ -116,9 +131,14 @@
<div class="divselect" id="divbranch" style="width:135px;">
<select id="bank_branch_id" name="bank_branch_id" style="width:153px" onblur="validate(this,'t','');">
<option value="-1" selected >SELECT</option>
<c:forEach items="${branchlist}" var="brnch">
<option value="${brnch[0]}">${brnch[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}">
<c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'branch.')}">
<option value="${fn:substringAfter(option.key, 'branch.')}"><c:out value="${option.value}" /></option>
</c:if></c:forEach>
</c:when>
<c:otherwise><c:forEach items="${branchlist}" var="brnch"><option value="${brnch[0]}"><c:out value="${brnch[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -131,9 +151,10 @@
<div class="divselect" id="divproduct" style="width:132px;">
<select id="product" name="product" style="width:150px" onblur="validate(this,'t','');">
<option value="-1" selected >SELECT</option>
<c:forEach items="${prodlist}" var="prod">
<option value="${prod[0]}">${prod[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'product.')}"><option value="${fn:substringAfter(option.key, 'product.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${prodlist}" var="prod"><option value="${prod[0]}"><c:out value="${prod[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -162,9 +183,10 @@
<div class="divselect" id="divapptype" style="width:135px;">
<select id="apptype" name="apptype" style="width:153px" onchange="ToggleChks((this.value.toUpperCase()!='APPLICANT' && this.value.toUpperCase()!='-1'),'sradd!C0L!soadd!C0L!spadd!C0L!','!C0L!')" onblur="validate(this,'t','');">
<option value="-1" selected>SELECT</option>
<c:forEach items="${typelist}" var="type">
<option value="${type[0]}">${type[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'applicationType.')}"><option value="${fn:substringAfter(option.key, 'applicationType.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${typelist}" var="type"><option value="${type[0]}"><c:out value="${type[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -177,9 +199,10 @@
<div class="divselect" id="divcategory" style="width:115px;">
<select id="category" name="category" style="width:133px">
<option value="-1" selected>SELECT</option>
<c:forEach items="${catlist}" var="cat">
<option value="${cat[0]}">${cat[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'category.')}"><option value="${fn:substringAfter(option.key, 'category.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${catlist}" var="cat"><option value="${cat[0]}"><c:out value="${cat[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -287,9 +310,10 @@
<select id="rcity" name="colr_citylist" style="width:128px" onblur="HandleBlur(this)" >
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:forEach items="${citylist}" var="city">
<option value="${city[0]}">${city[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -347,7 +371,7 @@
<span class="matrix-title-text">Office Details</span>
<div class="divchk" id="ctrldiv">
<input type="checkbox" id="ov" onclick="HandleClick(this)" />OVR&nbsp;
<input type="checkbox" id="rco" style="display:none" onclick="HandleClick(this)" /><span id="rco">RCO</span>&nbsp;
<input type="checkbox" id="rco" style="display:none" onclick="HandleClick(this)" /><span id="rco">RCO&nbsp;</span>
<input type="checkbox" id="soadd" onclick="return FindSameDet(this,'O','ophone')" /><span id="soadd">Same Office</span>
<input type="checkbox" id="otv" onclick="HandleClick(this)" /><span id="otv">OTV</span>
</div>
@@ -404,9 +428,10 @@
<select id="ocity" name="colo_citylist" style="width:128px" onblur="HandleBlur(this)" >
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:forEach items="${citylist}" var="city">
<option value="${city[0]}">${city[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -482,8 +507,8 @@
<span class="matrix-title-text">Property Details</span>
<div class="divchk" id="ctrldiv">
<input type="checkbox" id="pv" onclick="HandleClick(this)" />PVR&nbsp;
<input type="checkbox" id="rcp" style="display:none" onclick="HandleClick(this)" /><span id="rcp">RCP</span>&nbsp;
<input type="checkbox" id="ocp" style="display:none" onclick="HandleClick(this)" /><span id="ocp">OCP</span>&nbsp;
<input type="checkbox" id="rcp" style="display:none" onclick="HandleClick(this)" /><span id="rcp">RCP&nbsp;</span>
<input type="checkbox" id="ocp" style="display:none" onclick="HandleClick(this)" /><span id="ocp">OCP&nbsp;</span>
<input type="checkbox" id="spadd" onclick="return FindSameDet(this,'P','ppincode')" /><span id="spadd">Same Property</span>
</div>
</div>
@@ -530,9 +555,10 @@
<select id="pcity" name="colp_citylist" style="width:128px" onblur="HandleBlur(this)" >
<option value="-1">Select</option>
<option value="Unknown">Unknown</option>
<c:forEach items="${citylist}" var="city">
<option value="${city[0]}">${city[1]}</option>
</c:forEach>
<c:choose>
<c:when test="${not empty model}"><c:forEach items="${model.options}" var="option"><c:if test="${fn:startsWith(option.key, 'city.')}"><option value="${fn:substringAfter(option.key, 'city.')}"><c:out value="${option.value}" /></option></c:if></c:forEach></c:when>
<c:otherwise><c:forEach items="${citylist}" var="city"><option value="${city[0]}"><c:out value="${city[1]}" /></option></c:forEach></c:otherwise>
</c:choose>
</select>
</div>
</div>
@@ -621,12 +647,15 @@
<div id="section6">
<!-- Title -->
<div class="title">
<img class="matrix-title-icon" src="/matrix/images/othdet.png" />
<img class="matrix-title-icon" src="/matrix/images/edit_form.png" alt="" />
<span class="matrix-title-text">Other Details</span>
</div>
<!-- -->
<div id="FormPanel6" class="FormPanel">
${dynamichtml}
<c:choose>
<c:when test="${not empty model}"><c:out value="${model.dynamicHtml}" escapeXml="false" /></c:when>
<c:otherwise><c:out value="${dynamichtml}" escapeXml="false" /></c:otherwise>
</c:choose>
<br/>
<br/>
<br/>
@@ -650,16 +679,22 @@
<br />
<br />
<br />
<input type="hidden" id="dynamicfields" name="dynamicfields" value="${dynamicfields}" />
<input type="hidden" id="dynamicfields" name="dynamicfields" value="${pageDynamicFields}" />
<input type="hidden" id="visiblecontents" name="visiblecontents" value="${visiblecontents}" />
<input type="hidden" id="uuid" name="uuid" value="${duuid}" />
<input type="hidden" id="initial-portfolio-id" value="${pagePortfolioId}" />
<c:if test="${not empty model}">
<c:forEach items="${model.visibleSections}" var="section">
<span hidden class="model-visible-section"><c:out value="${section}" /></span>
</c:forEach>
</c:if>
<input type="hidden" id="uuid" name="uuid" value="${pageDocumentCaseId}" />
<input type="hidden" id="case_id" name="case_id" value="" />
<input type="hidden" id="company_id" name="company_id" value="${Sessvals.getCompanyID()}" />
<input type="hidden" id="branch_id" name="branch_id" value="${Sessvals.getBranchID()}" />
<input type="hidden" id="status" name="status" value="" />
<!-- -->
<input type="hidden" id="invalidfields" value="0" />
<c:if test="${formmode gt 0}">
<c:if test="${pageFormMode gt 0}">
<div style="position:fixed;text-align:right;bottom:0px;width:980px;" class="buttonbar">
<input type="hidden" id="invalidfields" value="0" />
<input type="button" class="button" name="btnfirst" style="margin-left:5px;margin-top:-2px;float:left;width:80px" value="" id="btnfirst" accesskey="F" onclick="findRecord(1)" />
@@ -667,25 +702,40 @@
<input type="button" class="button" name="btnnext" style="margin-top:-2px;float:left;width:80px" value="" id="btnnext" accesskey="N" onclick="findRecord(3)" />
<input type="button" class="button" name="btnlast" style="margin-top:-2px;float:left;width:80px" value="" id="btnlast" accesskey="L" onclick="findRecord(4)" />
<input type="button" class="button" name="btnsave" style="float:right" value="Save" id="btnsave" accesskey="S" onclick="return ValidateSubmitForm('casedetails');" />
<c:if test="${formmode!=2}">
<c:if test="${pageFormMode!=2}">
<input type="button" class="button" name="btnadd" style="float:right" value="Add" id="btnadd" accesskey="A" onclick="return AddNewRecord();" />
</c:if>
</div>
</c:if>
</form>
<input type="hidden" id="formmode" name="formmode" value="${formmode}" />
<input type="hidden" id="usid" name="usid" value="${usid}" />
<input type="hidden" id="formmode" name="formmode" value="${pageFormMode}" />
<input type="hidden" id="usid" name="usid" value="${pageUserId}" />
<c:if test="${not empty model.errorDetails}">
<span id="page-error-code" hidden><c:out value="${model.errorDetails.errorCode}" /></span>
<span id="page-error-message" hidden><c:out value="${model.errorDetails.message}" /></span>
</c:if>
</div>
</div>
</body>
<!-- Page Load Javascript -->
<script language="javascript" type="text/javascript">
InitPage('${portfolio_id}','${visiblecontents}');
(function () {
var sectionFields = document.querySelectorAll(".model-visible-section");
if (sectionFields.length > 0) {
document.getElementById("visiblecontents").value = Array.prototype.map.call(
sectionFields, function (field) { return field.textContent; }
).join(ColDelim);
}
InitPage();
var errorCode = document.getElementById("page-error-code");
var errorMessage = document.getElementById("page-error-message");
if (errorCode && errorMessage) {
CallMessage(errorCode.textContent + ":error:" + errorMessage.textContent, 3000, 200, 300);
}
}());
</script>
<!-- -->
<!-- Process Message -->
<c:if test="${not empty msg}">
<script language="javascript" type="text/javascript"> CallMessage('${msg}',3000,200,300); </script>
</c:if>
<!-- -->
</html>
</html>

View File

@@ -17,7 +17,7 @@
<script language="javascript" src="/matrix/js/ajax.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ajax-dynamic-list.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/appjs/punchingdoc.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/validator.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/validator.js?ver=2" type="text/javascript"></script>
<link href="/matrix/css/button.css" rel="stylesheet" type="text/css" />
<link href="/matrix/css/div.css" rel="stylesheet" type="text/css" />
@@ -30,7 +30,7 @@
<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/punch-workspace-v3.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.css?v=1" rel="stylesheet" type="text/css" />
<link href="/matrix/css/add-cases-v4.css?v=5" 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

@@ -1,29 +1,67 @@
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<script language="javascript" src="/matrix/js/lib/constants.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/lib/jsfuncs.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/jquery1.7.2.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/ui/uifuncs.js" type="text/javascript"></script>
<script language="javascript" src="/matrix/js/validator.js" type="text/javascript"></script>
<link href="/matrix/css/matrix.css" rel="stylesheet" type="text/css" />
<link href="/matrix/css/div.css" rel="stylesheet" type="text/css" />
<title>Cygnus 1.0 | 404 Error</title>
<script src="/matrix/js/matrix-accessibility-v1.js?v=1" defer></script>
<link href="/matrix/css/matrix-theme-v2.css?v=4" rel="stylesheet" type="text/css" />
</head>
<body class="matrix-error-page">
<form method="post" name="unaccess" id="unaccess">
<main class="matrix-error-card" role="alert" aria-labelledby="errorTitle">
<div class="matrix-error-card__code">404</div>
<h1 id="errorTitle">Page unavailable</h1>
<p>Unauthorized access or the requested page could not be found.</p>
<button type="button" class="matrix-error-card__action" onclick="SubmitForm('logout', '_parent', 'unaccess');">Return to sign in</button>
</main>
</form>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link href="<c:url value='/css/bootstrap-5.3.8.min.css' />" rel="stylesheet" />
<link href="<c:url value='/css/matrix-v2.css' />" rel="stylesheet" />
<link href="<c:url value='/css/matrix-shell-v2.css' />" rel="stylesheet" />
<link href="<c:url value='/css/matrix-theme-v2.css' />" rel="stylesheet" />
<script src="<c:url value='/js/matrix-shell-v2.js' />" defer></script>
<title>Cygnus 1.0 | <c:out value="${empty errorDetails.title ? 'Page unavailable' : errorDetails.title}" /></title>
</head>
<body class="matrix-v2 matrix-shell matrix-error-page matrix-error-page--${empty errorDetails.httpStatus ? 404 : errorDetails.httpStatus}">
<c:choose>
<c:when test="${not empty Sessvals and not empty Sessvals.menuHtml}">
<%@ include file="/WEB-INF/app/fragments/app-shell-header.jspf" %>
</c:when>
<c:otherwise>
<%@ include file="/WEB-INF/app/fragments/app-title.jspf" %>
</c:otherwise>
</c:choose>
<main class="matrix-error-layout" id="mainContent">
<section class="matrix-error-card" role="alert" aria-labelledby="errorTitle">
<div class="matrix-error-card__status" aria-hidden="true">
<span><c:out value="${empty errorDetails.httpStatus ? 404 : errorDetails.httpStatus}" /></span>
</div>
<div class="matrix-error-card__content">
<span class="matrix-error-card__eyebrow">
Error <c:out value="${empty errorDetails.errorCode ? 'HTTP-404' : errorDetails.errorCode}" />
</span>
<h1 id="errorTitle">
<c:out value="${empty errorDetails.title ? 'Page unavailable' : errorDetails.title}" />
</h1>
<p class="matrix-error-card__message">
<c:out value="${empty errorDetails.message ? 'The requested page is unavailable.' : errorDetails.message}" />
</p>
<c:if test="${not empty errorDetails.description}">
<p class="matrix-error-card__description"><c:out value="${errorDetails.description}" /></p>
</c:if>
<div class="matrix-error-card__actions">
<c:choose>
<c:when test="${not empty Sessvals and not empty Sessvals.menuHtml}">
<form method="post" action="<c:url value='/ver/dashboard' />">
<button type="submit" class="matrix-error-card__action">Return to dashboard</button>
</form>
<button type="button" class="matrix-error-card__action matrix-error-card__action--secondary" onclick="history.back()">Go back</button>
</c:when>
<c:otherwise>
<a class="matrix-error-card__action" href="<c:url value='/ver/login' />">Return to sign in</a>
</c:otherwise>
</c:choose>
</div>
<c:if test="${not empty errorDetails.referenceId}">
<p class="matrix-error-card__reference">
Reference ID: <code><c:out value="${errorDetails.referenceId}" /></code>
</p>
</c:if>
</div>
</section>
</main>
</body>
</html>

View File

@@ -37,7 +37,15 @@
<session-timeout>180</session-timeout>
</session-config>
<!-- Error Page -->
<!-- Error Page -->
<error-page>
<error-code>404</error-code>
<location>/ver/error</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/ver/error</location>
</error-page>
<error-page>
<exception-type>org.springframework.web.HttpSessionRequiredException</exception-type>
<location>/matrix/index.jsp</location>

View File

@@ -31,6 +31,12 @@
margin: 0;
}
/* Legacy forms separate labels and controls with a BR. In the modern flex
layout that BR becomes an extra flex row and creates an unintended gap. */
.matrix-add-cases .matrix-form-controls .widget > br {
display: none;
}
.matrix-add-cases .matrix-form-controls .widget[style*="display:none"] {
display: none !important;
}
@@ -89,3 +95,87 @@
.matrix-add-cases .matrix-form-controls .widget:has(#splist),
.matrix-add-cases .matrix-form-controls .widget:has(textarea) { grid-column: span 6; }
}
.matrix-add-cases .inputcontainer {
position: relative;
}
.matrix-add-cases .inputcontainer > input.errtxt,
.matrix-add-cases .inputcontainer > textarea.errtxt {
padding-right: 24px !important;
}
.matrix-add-cases .matrix-validation-error-icon {
display: block;
margin: 0 !important;
object-fit: contain;
}
.matrix-add-cases #section2 > .title,
.matrix-add-cases #section3 > .title,
.matrix-add-cases #section4 > .title,
.matrix-add-cases #section5 > .title,
.matrix-add-cases #section6 > .title,
.matrix-add-cases #section7 > .title {
box-sizing: border-box;
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 6px 8px;
min-height: 38px;
height: auto !important;
padding: 5px 10px !important;
}
.matrix-add-cases #section2 > .title > .matrix-title-icon,
.matrix-add-cases #section3 > .title > .matrix-title-icon,
.matrix-add-cases #section4 > .title > .matrix-title-icon,
.matrix-add-cases #section5 > .title > .matrix-title-icon,
.matrix-add-cases #section6 > .title > .matrix-title-icon,
.matrix-add-cases #section7 > .title > .matrix-title-icon {
display: block;
flex: 0 0 16px;
width: 16px;
height: 16px;
margin: 0 !important;
object-fit: contain;
vertical-align: middle;
}
.matrix-add-cases #section2 > .title > .matrix-title-text,
.matrix-add-cases #section3 > .title > .matrix-title-text,
.matrix-add-cases #section4 > .title > .matrix-title-text,
.matrix-add-cases #section5 > .title > .matrix-title-text,
.matrix-add-cases #section6 > .title > .matrix-title-text,
.matrix-add-cases #section7 > .title > .matrix-title-text {
line-height: 16px;
}
.matrix-add-cases #section2 > .title > .divchk,
.matrix-add-cases #section3 > .title > .divchk,
.matrix-add-cases #section4 > .title > .divchk,
.matrix-add-cases #section5 > .title > .divchk,
.matrix-add-cases #section7 > .title > .divchk {
position: static !important;
right: auto !important;
float: none !important;
display: inline-flex;
align-items: center;
align-self: center;
gap: 4px;
min-height: 18px;
margin: 0 0 0 auto !important;
padding: 0 !important;
line-height: 18px !important;
white-space: nowrap;
}
.matrix-add-cases #section2 > .title > .divchk input[type="checkbox"],
.matrix-add-cases #section3 > .title > .divchk input[type="checkbox"],
.matrix-add-cases #section4 > .title > .divchk input[type="checkbox"],
.matrix-add-cases #section5 > .title > .divchk input[type="checkbox"],
.matrix-add-cases #section7 > .title > .divchk input[type="checkbox"] {
flex: 0 0 auto;
margin-top: 0 !important;
margin-bottom: 0 !important;
vertical-align: middle;
}

View File

@@ -119,45 +119,103 @@ body .nav-menu {
}
body.matrix-error-page {
display: grid;
min-height: 100vh;
margin: 0;
padding: 24px;
place-items: center;
color: #263746;
background: var(--matrix-theme-canvas) !important;
font-family: Arial, sans-serif;
}
.matrix-error-card {
width: min(100%, 480px);
.matrix-error-layout {
min-height: calc(100vh - 84px);
display: grid;
place-items: center;
box-sizing: border-box;
padding: clamp(24px, 5vw, 64px) 20px;
}
.matrix-error-card {
width: min(100%, 760px);
display: grid;
grid-template-columns: 150px minmax(0, 1fr);
overflow: hidden;
box-sizing: border-box;
padding: 30px;
text-align: center;
border: 1px solid var(--matrix-theme-border);
border-radius: 8px;
border-radius: 10px;
background: var(--matrix-theme-panel);
box-shadow: 0 8px 24px rgba(31, 52, 72, .12);
}
.matrix-error-card__code {
color: #b42318;
font-size: 38px;
.matrix-error-card__status {
min-height: 290px;
display: grid;
place-items: center;
color: #fff;
background: #a4473f;
}
.matrix-error-page--401 .matrix-error-card__status,
.matrix-error-page--403 .matrix-error-card__status {
background: #a66a19;
}
.matrix-error-page--404 .matrix-error-card__status,
.matrix-error-page--405 .matrix-error-card__status {
background: #376b94;
}
.matrix-error-card__status span {
font-size: 42px;
font-weight: 800;
line-height: 1;
}
.matrix-error-card h1 {
margin: 10px 0 6px;
font-size: 21px;
.matrix-error-card__content {
padding: 34px 38px 28px;
}
.matrix-error-card p {
margin: 0 0 20px;
.matrix-error-card__eyebrow {
color: #607587;
font-size: 12px;
font-weight: 700;
letter-spacing: .08em;
text-transform: uppercase;
}
.matrix-error-card h1 {
margin: 8px 0 10px;
font-size: 25px;
}
.matrix-error-card__message {
margin: 0 0 8px;
color: #344b5e;
font-size: 15px;
font-weight: 600;
}
.matrix-error-card__description {
margin: 0;
color: #536779;
line-height: 1.55;
}
.matrix-error-card__actions {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 9px;
margin-top: 24px;
}
.matrix-error-card__actions form {
margin: 0;
}
.matrix-error-card__action {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 34px;
padding: 6px 16px;
color: #fff;
@@ -166,6 +224,55 @@ body.matrix-error-page {
background: #245a91;
font-weight: 700;
cursor: pointer;
text-decoration: none;
}
.matrix-error-card__action:hover,
.matrix-error-card__action:focus {
color: #fff;
background: #1d4e7d;
}
.matrix-error-card__action--secondary {
color: #334b5e;
border-color: #aebdca;
background: transparent;
}
.matrix-error-card__action--secondary:hover,
.matrix-error-card__action--secondary:focus {
color: #263746;
background: #e4edf4;
}
.matrix-error-card__reference {
margin: 22px 0 0;
padding-top: 14px;
color: #6b7e8e;
border-top: 1px solid #d5e0e8;
font-size: 11px;
}
.matrix-error-card__reference code {
color: inherit;
}
@media (max-width: 620px) {
.matrix-error-card {
grid-template-columns: 1fr;
}
.matrix-error-card__status {
min-height: 82px;
}
.matrix-error-card__status span {
font-size: 30px;
}
.matrix-error-card__content {
padding: 26px 24px;
}
}
/* Compact toolbars for legacy forms that place controls in their title bar. */

View File

@@ -12,14 +12,25 @@ AddrElem[1]=["ov","companyname","oaddr1","oaddr2","oaddr3","olandmark","Ocolony"
AddrElem[2]=["pv","paddr1","paddr2","paddr3","plandmark","Pcolony","colp","pcity","ppincode"];
AddrElem[3]=["bankcode","bankcode","bank_branch_id","product","loanamount","apptype","contactperson","specialinst"];
function InitPage(port,viscontents)
{
centerDivBoth("formcontainer","H");
function InitPage()
{
var portField = document.getElementById("portfolio_id");
var initialPortField = document.getElementById("initial-portfolio-id");
var visibleContentsField = document.getElementById("visiblecontents");
var port = initialPortField ? initialPortField.value : (portField ? portField.value : "-1");
var viscontents = visibleContentsField ? visibleContentsField.value : "";
if (viscontents && viscontents.slice(-ColDelim.length) !== ColDelim) {
viscontents += ColDelim;
}
centerDivBoth("formcontainer","H");
HideSections('rtv');
HideSections('otv');
HideSections('section');
UnhideSections(viscontents,ColDelim);
$("#portfolio_id").val(port);
if (portField) {
$(portField).val(port);
}
if(port>0)
{
if(document.getElementById("ajaxmsg") !== null)

View File

@@ -82,39 +82,44 @@ function GetRegExp(el,ValidFormat)
if(ValidFormat=='PAN'){return /^(([a-zA-Z]{5})\d{4}([a-zA-Z]{1}))$/;}
if(ValidFormat=='CreditCard'){return /^\d{16}$/;}
}
function highlightField(el)
{
function highlightField(el)
{
if (document.getElementById('err'+el.id))
{
//nothing to do already exists
}
else
{
invalidFields=invalidFields+1;
var offset=null;
var width=null;
if(el.type=='text' || el.type=='textarea' || el.type=='password')
{
el.setAttribute("class", "textbox errtxt");
offset=$(el).offset();
width=el.offsetWidth;
}
else if(el.type=='select-one')
{
el.parentNode.setAttribute("class", "divselect errdiv");
tmpel=el.parentNode;
offset=$(tmpel).offset();
width=tmpel.offsetWidth;
}
var top=parseInt(offset.top)-5;
var left=parseInt(offset.left)+(width-5);
var img=document.createElement("img");
img.setAttribute("src","/matrix/images/exclamation.png");
img.setAttribute("style","position:absolute;margin-left:"+left+"px;margin-top:"+top+"px;z-index:3");
img.setAttribute("id","err"+el.id);
document.body.appendChild(img);
}
}
else
{
invalidFields=invalidFields+1;
var iconHost=el.parentNode;
var current=el.parentNode;
while(current && current!==document.body)
{
var classes=" "+(current.className || "")+" ";
if(classes.indexOf(" inputcontainer ")!==-1)
{
iconHost=current;
break;
}
current=current.parentNode;
}
if(el.type=='text' || el.type=='textarea' || el.type=='password')
{
el.setAttribute("class", "textbox errtxt");
}
else if(el.type=='select-one')
{
el.parentNode.setAttribute("class", "divselect errdiv");
}
iconHost.style.position="relative";
var img=document.createElement("img");
img.setAttribute("src","/matrix/images/exclamation.png");
img.setAttribute("class", "matrix-validation-error-icon"+(el.type=='select-one' ? " matrix-validation-error-icon--select" : ""));
img.setAttribute("style","position:absolute;right:"+(el.type=='select-one' ? "24px" : "5px")+";top:50%;width:14px;height:14px;transform:translateY(-50%);z-index:3;pointer-events:none");
img.setAttribute("id","err"+el.id);
iconHost.appendChild(img);
}
}
// DATE FORMAT VALIDATOR DD/MM/YYYY
function addSlashes(el,evt)

View File

@@ -23,6 +23,16 @@
</properties>
<dependencies>
<dependency>
<groupId>com.cygnus</groupId>
<artifactId>cygnus-lib</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.cygnus</groupId>
<artifactId>cygnus-onprem-db</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.cygnus</groupId>
<artifactId>cygnus-cloud-client</artifactId>

View File

@@ -0,0 +1,63 @@
package matrix.nimble.controller;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lib.constants.ApplicationError;
import lib.models.ErrorDetails;
import matrix.nimble.model.Session;
import matrix.services.commons.CommonErrorService;
import matrix.services.commons.CommonService;
import org.springframework.ui.ModelMap;
/** Shared session and page-authorization workflow for authenticated controllers. */
public abstract class AbstractAuthenticatedController {
private final CommonService commonService;
private final CommonErrorService errorService;
protected AbstractAuthenticatedController(
CommonService commonService,
CommonErrorService errorService) {
this.commonService = commonService;
this.errorService = errorService;
}
protected final PageAuthorization authorizePage(
String pageRoute,
ModelMap model,
HttpSession httpSession,
HttpServletResponse response) {
Session session = commonService.getSession(httpSession);
if (session == null) {
return denied(model, response, httpSession, ApplicationError.SESSION_REQUIRED);
}
if (!commonService.hasPageAccess(session, pageRoute)) {
return denied(model, response, httpSession, ApplicationError.ACCESS_DENIED);
}
model.addAttribute(CommonService.SESSION_ATTRIBUTE, session);
return PageAuthorization.granted(session);
}
private PageAuthorization denied(
ModelMap model,
HttpServletResponse response,
HttpSession httpSession,
ApplicationError error) {
String viewName = errorService.render(
model, response, httpSession, new ErrorDetails(error));
return PageAuthorization.denied(viewName);
}
protected record PageAuthorization(Session session, String viewName) {
private static PageAuthorization granted(Session session) {
return new PageAuthorization(session, null);
}
private static PageAuthorization denied(String viewName) {
return new PageAuthorization(null, viewName);
}
public boolean isGranted() {
return session != null;
}
}
}

View File

@@ -0,0 +1,43 @@
package matrix.nimble.controller;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lib.models.ErrorDetails;
import lib.constants.ApplicationError;
import matrix.services.commons.CommonErrorService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ApplicationErrorController {
private final CommonErrorService errorService;
public ApplicationErrorController(CommonErrorService errorService) {
this.errorService = errorService;
}
@RequestMapping("error")
public String error(
ModelMap model,
HttpServletRequest request,
HttpServletResponse response,
HttpSession httpSession) {
int status = status(request);
return errorService.render(model, response, httpSession, details(status));
}
private int status(HttpServletRequest request) {
Object value = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (value instanceof Integer status && status >= 400 && status <= 599) {
return status;
}
return HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
}
private ErrorDetails details(int status) {
return new ErrorDetails(ApplicationError.fromHttpStatus(status));
}
}

View File

@@ -4,6 +4,7 @@ import matrix.nimble.cloud.identity.CloudAuthenticationException;
import matrix.nimble.cloud.identity.CloudAuthenticationGateway;
import matrix.nimble.model.Login;
import matrix.nimble.model.Session;
import matrix.services.commons.CommonService;
//servlet libraries
import jakarta.servlet.http.HttpServletRequest;
@@ -21,9 +22,13 @@ import org.springframework.web.bind.annotation.SessionAttributes;
@SessionAttributes("Sessvals")
public class SessionController {
private final CloudAuthenticationGateway cloudAuthenticationGateway;
private final CommonService commonService;
public SessionController(CloudAuthenticationGateway cloudAuthenticationGateway) {
public SessionController(
CloudAuthenticationGateway cloudAuthenticationGateway,
CommonService commonService) {
this.cloudAuthenticationGateway = cloudAuthenticationGateway;
this.commonService = commonService;
}
@RequestMapping(value="login", method={RequestMethod.GET, RequestMethod.POST})
public String LoginPage(ModelMap model)
@@ -42,13 +47,17 @@ public class SessionController {
return "login";
}
@RequestMapping(value="authenticatelogin",method=RequestMethod.POST )
public String Authenticate(ModelMap model,@ModelAttribute(value="login") Login login)
public String Authenticate(
ModelMap model,
@ModelAttribute(value="login") Login login,
HttpSession httpSession)
{
try {
Session cloudSession = cloudAuthenticationGateway.authenticate(
login.getLoginid(), login.getPassword());
model.remove("login");
model.addAttribute("Sessvals", cloudSession);
commonService.storeSession(httpSession, cloudSession);
return "home";
} catch (CloudAuthenticationException exception) {
login.setErrMsg(exception.reason()

View File

@@ -1,161 +0,0 @@
package matrix.nimble.edp.punching.controller;
//servlet libraries
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
//spring libraries
import matrix.nimble.edp.punching.model.CaseDetails;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.ModuleFunctions;
import matrix.nimble.utilities.GlobalClass;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
@SessionAttributes({"Sessvals"})
public class CasePucnhing {
@RequestMapping(value="caseadd1",method=RequestMethod.POST )
public String OpenAddCases(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
CaseDetails caseDetails=new CaseDetails();
caseDetails.setFormMode(1);
caseDetails.setErrMsg("");
model.addAttribute("visibleContents","");
model.addAttribute("caseDetails",caseDetails);
model.addAttribute("branchlist",FillBranchList("0"));
model.addAttribute("prodlist",FillProductList("0"));
model.addAttribute("citylist",FillCityList("0"));
model.addAttribute("typelist",FillApptypeList("0"));
model.addAttribute("doclist",FillDocList("0"));
model.addAttribute("catlist",FillCatList("0"));
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("portlist",FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
@RequestMapping(value="casesave",method=RequestMethod.POST )
public String SaveCase(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,@ModelAttribute(value="caseDetails") CaseDetails caseDet,HttpSession session)
{
String DynamicNsessVals="";
CaseDetails caseDetails=new CaseDetails();
caseDet.setErrCode("1001");
if(caseDet.getDynamicFields()!="")
{
String [] DynamicFields=caseDet.getDynamicFields().split(GlobalClass.ColDelim);
for(int indx=0;indx<DynamicFields.length;indx++)
{
//DynamicNsessVals=DynamicNsessVals+request.getParameter(DynamicFields[indx])+Delimeter.ColDelim;
caseDet.InvokeSetter("set"+DynamicFields[indx], request.getParameter(DynamicFields[indx]));
}
}
if(caseDet.getFormMode()==1 || caseDet.getFormMode()==2)
{
DynamicNsessVals=DynamicNsessVals+""+Sessvals.getUserID()+GlobalClass.ColDelim;
DynamicNsessVals=DynamicNsessVals+""+Sessvals.getCompanyID()+GlobalClass.ColDelim;
DynamicNsessVals=DynamicNsessVals+""+Sessvals.getBranchID();
}
if(!session.getAttribute("UUID").toString().equals(caseDet.getUUID()))
{
caseDetails.setProcessFlag(true);
caseDetails.setErrMsg("");
}
else
{
caseDet.SaveDetails(DynamicNsessVals);
caseDetails.setProcessFlag(caseDet.isProcessFlag());
caseDetails.setErrMsg(caseDet.getErrMsg());
}
String VisibleContents=caseDet.getVisibleContents();
if(caseDetails.isProcessFlag())
{
String UUID=GlobalClass.GenerateUUID();
caseDetails.setUUID(UUID);
session.setAttribute("UUID", UUID);
caseDetails.setErrCode("1001");
caseDetails.setFormMode(caseDet.getFormMode());
caseDetails.setVisibleContents(VisibleContents);
caseDetails.setPortfolioId(caseDet.getPortfolioId());
caseDetails=caseDetails.DynamicHtml(caseDetails,caseDet.getPortfolioId());
caseDetails.setDynamicFields(caseDet.getDynamicFields());
VisibleContents=VisibleContents+""+caseDetails.getDynamicSection();
model.addAttribute("caseDetails",caseDetails);
}
else
{
caseDet=caseDet.DynamicHtml(caseDet,caseDet.getPortfolioId());
VisibleContents=VisibleContents+""+caseDet.getDynamicSection();
model.addAttribute("caseDetails",caseDet);
}
model.addAttribute("visibleContents",VisibleContents);
model.addAttribute("branchlist",FillBranchList(caseDet.getPortfolioId()));
model.addAttribute("prodlist",FillProductList(caseDet.getPortfolioId()));
model.addAttribute("citylist",FillCityList(Sessvals.getBranchID()));
model.addAttribute("typelist",FillApptypeList(Sessvals.getBranchID()));
model.addAttribute("doclist",FillDocList(caseDet.getPortfolioId()));
model.addAttribute("catlist",FillCatList(caseDet.getPortfolioId()));
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("portlist",FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
@RequestMapping(value="casedet",method=RequestMethod.POST )
public String GetRequiredData(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,@ModelAttribute(value="caseDetails") CaseDetails caseDet,HttpSession session)
{
CaseDetails caseDetails = new CaseDetails();
caseDetails.setPortfolioId(caseDet.getPortfolioId());
caseDetails.setErrCode("1001");
String UUID=GlobalClass.GenerateUUID();
caseDetails.setUUID(UUID);
session.setAttribute("UUID", UUID);
caseDetails.setFormMode(caseDet.getFormMode());
caseDetails=caseDetails.DynamicHtml(caseDetails,caseDet.getPortfolioId());
String VisibleContents=(new ModuleFunctions("1001").GetDDHashString(10, ("10"+GlobalClass.ColDelim+caseDet.getPortfolioId()).split(GlobalClass.ColDelim))).replace(GlobalClass.RowDelim, "");
caseDetails.setVisibleContents(VisibleContents);
VisibleContents=(VisibleContents+""+caseDetails.getDynamicSection()).replace(GlobalClass.ColDelim+"null", "");
model.addAttribute("visibleContents",VisibleContents);
model.addAttribute("caseDetails",caseDetails);
model.addAttribute("portlist",Sessvals.getBranchID());
model.addAttribute("branchlist",FillBranchList(caseDet.getPortfolioId()));
model.addAttribute("prodlist",FillProductList(caseDet.getPortfolioId()));
model.addAttribute("citylist",FillCityList(Sessvals.getBranchID()));
model.addAttribute("typelist",FillApptypeList(Sessvals.getBranchID()));
model.addAttribute("doclist",FillDocList(caseDet.getPortfolioId()));
model.addAttribute("catlist",FillCatList(caseDet.getPortfolioId()));
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("portlist",FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
public String [][] FillPortList(String BranchId)
{
return new ModuleFunctions("1001").GetResultArray(3,(BranchId+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
}
public String [][] FillCityList(String BranchID)
{
return new ModuleFunctions("1001").GetResultArray(6,BranchID.split(GlobalClass.ColDelim));
}
public String [][] FillApptypeList(String BranchID)
{
return new ModuleFunctions("1001").GetResultArray(8,BranchID.split(GlobalClass.ColDelim));
}
public String [][] FillCatList(String PortfolioID)
{
return new ModuleFunctions("1001").GetResultArray(9,PortfolioID.split(GlobalClass.ColDelim));
}
public String [][] FillDocList(String PortfolioID)
{
return new ModuleFunctions("1001").GetResultArray(7,PortfolioID.split(GlobalClass.ColDelim));
}
public String [][] FillBranchList(String PortfolioID)
{
return new ModuleFunctions("1001").GetResultArray(4,PortfolioID.split(GlobalClass.ColDelim));
}
public String [][] FillProductList(String PortfolioID)
{
return new ModuleFunctions("1001").GetResultArray(5,PortfolioID.split(GlobalClass.ColDelim));
}
}

View File

@@ -1,60 +0,0 @@
package matrix.nimble.edp.punching.controller;
import java.util.HashMap;
import jakarta.servlet.http.HttpServletRequest;
import matrix.nimble.edp.punching.model.CaseGrid;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.GlobalClass;
import matrix.nimble.utilities.ModuleFunctions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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;
@Controller
@SessionAttributes({"Sessvals"})
public class EditCasesGrid {
@RequestMapping(value="editgrid",method=RequestMethod.POST )
public String OpenEditGrid(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
String [][]CaseList=null;
CaseGrid CG=new CaseGrid();
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("caseList",CaseList);
model.addAttribute("caseGrid",CG);
model.addAttribute("portlist",FillPortList(Sessvals.getBranchID(),Sessvals.getUserID()));
return "edp/punching/editcases";
}
@RequestMapping(value="caselist",method=RequestMethod.POST)
public String ListCases(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals,@ModelAttribute(value="caseGrid") CaseGrid caseGrid)
{
CaseGrid CG=new CaseGrid();
CG.setErrCode("1002");
CG.setPortfolioId(caseGrid.getPortfolioId());
String [][]CaseList=CG.FillCaseList(caseGrid.getPortfolioId(), Sessvals.getBranchID());
if(CG.isProcessFlag())
{
model.addAttribute("caseList",CaseList);
model.addAttribute("caseGrid",CG);
}
else
{
caseGrid.setErrMsg(CG.getErrMsg());
model.addAttribute("caseList",CaseList);
model.addAttribute("caseGrid",caseGrid);
}
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("portlist",FillPortList(Sessvals.getBranchID(),Sessvals.getUserID()));
return "edp/punching/editcases";
}
public String [][] FillPortList(String BranchId,String userId)
{
return new ModuleFunctions("1002").GetResultArray(249,(BranchId+GlobalClass.ColDelim+userId+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
}
}

View File

@@ -1,278 +0,0 @@
package matrix.nimble.edp.punching.controller;
//servlet libraries
import java.net.URLDecoder;
import jakarta.servlet.http.HttpServletRequest;
//spring libraries
import matrix.nimble.edp.punching.model.PunchingHandler;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.CommonFunctions;
import matrix.nimble.utilities.GlobalClass;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
@SessionAttributes({"Sessvals"})
public class Punching {
@RequestMapping(value="initview",method=RequestMethod.POST )
public String InitializeScreen(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
PunchingHandler PH=new PunchingHandler();
PH.setErrCode("1001");
model.addAttribute("visiblecontents","");
model.addAttribute("branchlist",null);
model.addAttribute("prodlist",null);
model.addAttribute("citylist",null);
model.addAttribute("typelist",null);
model.addAttribute("doclist",null);
model.addAttribute("catlist",null);
model.addAttribute("portfolio_id","-1");
model.addAttribute("Sessvals",Sessvals);
PH.GetDDValues(3, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist",PH.getOptionvals());
model.addAttribute("formmode",0);
model.addAttribute("stat",true);
model.addAttribute("duuid","xxx");
model.addAttribute("msg","");
return "edp/punching/initcase";
}
@RequestMapping(value="initdocview",method=RequestMethod.POST )
public String InitDocScreen(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
PunchingHandler PH=new PunchingHandler();
PH.setErrCode("1901");
model.addAttribute("branchlist",null);
model.addAttribute("prodlist",null);
model.addAttribute("typelist",null);
model.addAttribute("doclist",null);
model.addAttribute("catlist",null);
model.addAttribute("portfolio_id","-1");
model.addAttribute("Sessvals",Sessvals);
PH.GetDDValues(240, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist",PH.getOptionvals());
model.addAttribute("formmode",0);
model.addAttribute("stat",true);
model.addAttribute("msg","");
return "edp/punching/initdocs";
}
@RequestMapping(value="docadd",method=RequestMethod.POST )
public String PunchDocs(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
String msg="";
PunchingHandler PH=new PunchingHandler();
CommonFunctions comFunc=new CommonFunctions();
PH.setErrCode("1901");
PH.setPortfolioid(request.getParameter("portfolio_id"));
PH.setProcessFlag(true);
PH.DocDynamicHtml();
if(PH.isProcessFlag())
{
model.addAttribute("dynamichtml",PH.getDynamicpanel());
model.addAttribute("dynamicfields",PH.getDynamicfields());
model.addAttribute("docdynamichtml",PH.getDocdynamicpanel());
model.addAttribute("docdynamicfields",PH.getDocdynamicfields());
}
else
{
msg=PH.getErrMsg();
}
PH.GetDDValues(240, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist",PH.getOptionvals());
PH.GetDDValues(4, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("branchlist",PH.getOptionvals());
PH.GetDDValues(5, (Sessvals.getBranchID()+GlobalClass.ColDelim+"op.description='DOCUMENT' or op.description='APPTYPE'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("typelist",comFunc.GetSubArray("APPTYPE", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("typelist",null);
}
PH.GetDDValues(241, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("doclist",PH.getOptionvals());
PH.GetDDValues(70, (PH.getPortfolioid()+GlobalClass.ColDelim+"op.description='CATEGORY' or op.description='PRODUCT'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("prodlist",comFunc.GetSubArray("PRODUCT", 2, PH.getOptionvals()));
model.addAttribute("catlist",comFunc.GetSubArray("CATEGORY", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("prodlist",null);
model.addAttribute("catlist",null);
}
model.addAttribute("portfolio_id",PH.getPortfolioid());
model.addAttribute("usid",Sessvals.getUserID());
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("formmode",1);
model.addAttribute("stat",PH.isProcessFlag());
model.addAttribute("msg",msg);
return "edp/punching/initdocs";
}
@RequestMapping(value="addeditdoc",method=RequestMethod.POST )
public String AddEditDocs(@RequestParam String val0,@RequestParam String val1,@RequestParam String val2,@RequestParam String val3,@RequestParam String val4,@RequestParam String val5,@RequestParam String val6,@RequestParam String val7,@RequestParam String val8,@RequestParam String val9,@RequestParam String val10)
{
String retval="success";
try
{
//System.out.println(URLDecoder.decode(val0,"UTF-8"));
//System.out.println(URLDecoder.decode(val1,"UTF-8"));
//System.out.println(URLDecoder.decode(val2,"UTF-8"));
//System.out.println(URLDecoder.decode(val3,"UTF-8"));
//System.out.println(URLDecoder.decode(val4,"UTF-8"));
//System.out.println(URLDecoder.decode(val6,"UTF-8"));
//System.out.println(URLDecoder.decode(val7,"UTF-8"));
//System.out.println(URLDecoder.decode(val8,"UTF-8"));
//System.out.println(URLDecoder.decode(val9,"UTF-8"));
//System.out.println(URLDecoder.decode(val10,"UTF-8"));
PunchingHandler PH=new PunchingHandler();
PH.setErrCode("1902");
PH.setProcessFlag(true);
PH.AddEditDocs(val0, val1, val2, val3, val4, val6, val7, val8, val9, val10);
}catch(Exception exce)
{
retval=exce.getMessage();
}
return retval;
}
@RequestMapping(value="caseadd",method=RequestMethod.POST )
public String PunchCase(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
String msg="";
PunchingHandler PH=new PunchingHandler();
CommonFunctions comFunc=new CommonFunctions();
PH.setErrCode("1001");
PH.setPortfolioid(request.getParameter("portfolio_id"));
PH.setProcessFlag(true);
PH.DynamicHtml("10");
if(PH.isProcessFlag())
{
model.addAttribute("dynamicfields",PH.getDynamicfields());
if(PH.isProcessFlag())
{
model.addAttribute("visiblecontents",(PH.GetVisibleSections()+""+PH.getDynamicsection()).replace("null", ""));
model.addAttribute("dynamichtml",PH.getDynamicpanel());
}
else
{
msg=PH.getErrMsg();
}
}
else
{
msg=PH.getErrMsg();
}
PH.GetDDValues(3, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist",PH.getOptionvals());
PH.GetDDValues(4, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("branchlist",PH.getOptionvals());
PH.GetDDValues(5, (Sessvals.getBranchID()+GlobalClass.ColDelim+"op.description='CITY' or op.description='APPTYPE'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("citylist",comFunc.GetSubArray("CITY", 2, PH.getOptionvals()));
model.addAttribute("typelist",comFunc.GetSubArray("APPTYPE", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("citylist",null);
model.addAttribute("typelist",null);
}
PH.GetDDValues(70, (PH.getPortfolioid()+GlobalClass.ColDelim+"op.description='CATEGORY' or op.description='PRODUCT'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("prodlist",comFunc.GetSubArray("PRODUCT", 2, PH.getOptionvals()));
model.addAttribute("catlist",comFunc.GetSubArray("CATEGORY", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("prodlist",null);
model.addAttribute("catlist",null);
}
model.addAttribute("portfolio_id",PH.getPortfolioid());
model.addAttribute("doclist",null);
model.addAttribute("usid",Sessvals.getUserID());
model.addAttribute("duuid","xxx");
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("formmode",1);
model.addAttribute("stat",PH.isProcessFlag());
model.addAttribute("msg",msg);
return "edp/punching/initcase";
}
@RequestMapping(value="caseedit",method=RequestMethod.POST )
public String EditCase(ModelMap model,HttpServletRequest request,@ModelAttribute(value="Sessvals") Session Sessvals)
{
String msg="";
PunchingHandler PH=new PunchingHandler();
CommonFunctions comFunc=new CommonFunctions();
PH.setErrCode("1001");
PH.setPortfolioid(request.getParameter("PortfolioId"));
PH.setProcessFlag(true);
PH.DynamicHtml("10");
if(PH.isProcessFlag())
{
model.addAttribute("dynamicfields",PH.getDynamicfields());
if(PH.isProcessFlag())
{
model.addAttribute("visiblecontents",(PH.GetVisibleSections()+""+PH.getDynamicsection()).replace("null", ""));
model.addAttribute("dynamichtml",PH.getDynamicpanel());
}
else
{
msg=PH.getErrMsg();
}
}
else
{
msg=PH.getErrMsg();
}
PH.GetDDValues(3, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist",PH.getOptionvals());
PH.GetDDValues(4, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("branchlist",PH.getOptionvals());
PH.GetDDValues(5, (Sessvals.getBranchID()+GlobalClass.ColDelim+"op.description='CITY' or op.description='APPTYPE'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("citylist",comFunc.GetSubArray("CITY", 2, PH.getOptionvals()));
model.addAttribute("typelist",comFunc.GetSubArray("APPTYPE", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("citylist",null);
model.addAttribute("typelist",null);
}
PH.GetDDValues(70, (PH.getPortfolioid()+GlobalClass.ColDelim+"op.description='CATEGORY' or op.description='PRODUCT'"+GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if(PH.getOptionvals()!=null)
{
model.addAttribute("prodlist",comFunc.GetSubArray("PRODUCT", 2, PH.getOptionvals()));
model.addAttribute("catlist",comFunc.GetSubArray("CATEGORY", 2, PH.getOptionvals()));
}
else
{
model.addAttribute("prodlist",null);
model.addAttribute("catlist",null);
}
model.addAttribute("portfolio_id",PH.getPortfolioid());
model.addAttribute("doclist",null);
model.addAttribute("usid",Sessvals.getUserID());
model.addAttribute("Sessvals",Sessvals);
model.addAttribute("formmode",2);
model.addAttribute("duuid",request.getParameter("uuid"));
model.addAttribute("stat",PH.isProcessFlag());
model.addAttribute("msg",msg);
return "edp/punching/initcase";
}
}

View File

@@ -0,0 +1,160 @@
package matrix.nimble.edp.punching.controllers;
//servlet libraries
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
//spring libraries
import matrix.nimble.edp.punching.model.CaseDetails;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.ModuleFunctions;
import matrix.nimble.utilities.GlobalClass;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
@SessionAttributes({ "Sessvals" })
public class CasePucnhing {
@RequestMapping(value = "caseadd1", method = RequestMethod.POST)
public String OpenAddCases(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals) {
CaseDetails caseDetails = new CaseDetails();
caseDetails.setFormMode(1);
caseDetails.setErrMsg("");
model.addAttribute("visibleContents", "");
model.addAttribute("caseDetails", caseDetails);
model.addAttribute("branchlist", FillBranchList("0"));
model.addAttribute("prodlist", FillProductList("0"));
model.addAttribute("citylist", FillCityList("0"));
model.addAttribute("typelist", FillApptypeList("0"));
model.addAttribute("doclist", FillDocList("0"));
model.addAttribute("catlist", FillCatList("0"));
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("portlist", FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
@RequestMapping(value = "casesave", method = RequestMethod.POST)
public String SaveCase(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals,
@ModelAttribute(value = "caseDetails") CaseDetails caseDet, HttpSession session) {
String DynamicNsessVals = "";
CaseDetails caseDetails = new CaseDetails();
caseDet.setErrCode("1001");
if (caseDet.getDynamicFields() != "") {
String[] DynamicFields = caseDet.getDynamicFields().split(GlobalClass.ColDelim);
for (int indx = 0; indx < DynamicFields.length; indx++) {
// DynamicNsessVals=DynamicNsessVals+request.getParameter(DynamicFields[indx])+Delimeter.ColDelim;
caseDet.InvokeSetter("set" + DynamicFields[indx], request.getParameter(DynamicFields[indx]));
}
}
if (caseDet.getFormMode() == 1 || caseDet.getFormMode() == 2) {
DynamicNsessVals = DynamicNsessVals + "" + Sessvals.getUserID() + GlobalClass.ColDelim;
DynamicNsessVals = DynamicNsessVals + "" + Sessvals.getCompanyID() + GlobalClass.ColDelim;
DynamicNsessVals = DynamicNsessVals + "" + Sessvals.getBranchID();
}
if (!session.getAttribute("UUID").toString().equals(caseDet.getUUID())) {
caseDetails.setProcessFlag(true);
caseDetails.setErrMsg("");
} else {
caseDet.SaveDetails(DynamicNsessVals);
caseDetails.setProcessFlag(caseDet.isProcessFlag());
caseDetails.setErrMsg(caseDet.getErrMsg());
}
String VisibleContents = caseDet.getVisibleContents();
if (caseDetails.isProcessFlag()) {
String UUID = GlobalClass.GenerateUUID();
caseDetails.setUUID(UUID);
session.setAttribute("UUID", UUID);
caseDetails.setErrCode("1001");
caseDetails.setFormMode(caseDet.getFormMode());
caseDetails.setVisibleContents(VisibleContents);
caseDetails.setPortfolioId(caseDet.getPortfolioId());
caseDetails = caseDetails.DynamicHtml(caseDetails, caseDet.getPortfolioId());
caseDetails.setDynamicFields(caseDet.getDynamicFields());
VisibleContents = VisibleContents + "" + caseDetails.getDynamicSection();
model.addAttribute("caseDetails", caseDetails);
} else {
caseDet = caseDet.DynamicHtml(caseDet, caseDet.getPortfolioId());
VisibleContents = VisibleContents + "" + caseDet.getDynamicSection();
model.addAttribute("caseDetails", caseDet);
}
model.addAttribute("visibleContents", VisibleContents);
model.addAttribute("branchlist", FillBranchList(caseDet.getPortfolioId()));
model.addAttribute("prodlist", FillProductList(caseDet.getPortfolioId()));
model.addAttribute("citylist", FillCityList(Sessvals.getBranchID()));
model.addAttribute("typelist", FillApptypeList(Sessvals.getBranchID()));
model.addAttribute("doclist", FillDocList(caseDet.getPortfolioId()));
model.addAttribute("catlist", FillCatList(caseDet.getPortfolioId()));
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("portlist", FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
@RequestMapping(value = "casedet", method = RequestMethod.POST)
public String GetRequiredData(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals,
@ModelAttribute(value = "caseDetails") CaseDetails caseDet, HttpSession session) {
CaseDetails caseDetails = new CaseDetails();
caseDetails.setPortfolioId(caseDet.getPortfolioId());
caseDetails.setErrCode("1001");
String UUID = GlobalClass.GenerateUUID();
caseDetails.setUUID(UUID);
session.setAttribute("UUID", UUID);
caseDetails.setFormMode(caseDet.getFormMode());
caseDetails = caseDetails.DynamicHtml(caseDetails, caseDet.getPortfolioId());
String VisibleContents = (new ModuleFunctions("1001").GetDDHashString(10,
("10" + GlobalClass.ColDelim + caseDet.getPortfolioId()).split(GlobalClass.ColDelim)))
.replace(GlobalClass.RowDelim, "");
caseDetails.setVisibleContents(VisibleContents);
VisibleContents = (VisibleContents + "" + caseDetails.getDynamicSection())
.replace(GlobalClass.ColDelim + "null", "");
model.addAttribute("visibleContents", VisibleContents);
model.addAttribute("caseDetails", caseDetails);
model.addAttribute("portlist", Sessvals.getBranchID());
model.addAttribute("branchlist", FillBranchList(caseDet.getPortfolioId()));
model.addAttribute("prodlist", FillProductList(caseDet.getPortfolioId()));
model.addAttribute("citylist", FillCityList(Sessvals.getBranchID()));
model.addAttribute("typelist", FillApptypeList(Sessvals.getBranchID()));
model.addAttribute("doclist", FillDocList(caseDet.getPortfolioId()));
model.addAttribute("catlist", FillCatList(caseDet.getPortfolioId()));
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("portlist", FillPortList(Sessvals.getBranchID()));
return "edp/punching/addcases";
}
public String[][] FillPortList(String BranchId) {
return new ModuleFunctions("1001").GetResultArray(3,
(BranchId + GlobalClass.ColDelim).split(GlobalClass.ColDelim));
}
public String[][] FillCityList(String BranchID) {
return new ModuleFunctions("1001").GetResultArray(6, BranchID.split(GlobalClass.ColDelim));
}
public String[][] FillApptypeList(String BranchID) {
return new ModuleFunctions("1001").GetResultArray(8, BranchID.split(GlobalClass.ColDelim));
}
public String[][] FillCatList(String PortfolioID) {
return new ModuleFunctions("1001").GetResultArray(9, PortfolioID.split(GlobalClass.ColDelim));
}
public String[][] FillDocList(String PortfolioID) {
return new ModuleFunctions("1001").GetResultArray(7, PortfolioID.split(GlobalClass.ColDelim));
}
public String[][] FillBranchList(String PortfolioID) {
return new ModuleFunctions("1001").GetResultArray(4, PortfolioID.split(GlobalClass.ColDelim));
}
public String[][] FillProductList(String PortfolioID) {
return new ModuleFunctions("1001").GetResultArray(5, PortfolioID.split(GlobalClass.ColDelim));
}
}

View File

@@ -0,0 +1,60 @@
package matrix.nimble.edp.punching.controllers;
import java.util.HashMap;
import jakarta.servlet.http.HttpServletRequest;
import matrix.nimble.edp.punching.model.CaseGrid;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.GlobalClass;
import matrix.nimble.utilities.ModuleFunctions;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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;
@Controller
@SessionAttributes({ "Sessvals" })
public class EditCasesGrid {
@RequestMapping(value = "editgrid", method = RequestMethod.POST)
public String OpenEditGrid(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals) {
String[][] CaseList = null;
CaseGrid CG = new CaseGrid();
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("caseList", CaseList);
model.addAttribute("caseGrid", CG);
model.addAttribute("portlist", FillPortList(Sessvals.getBranchID(), Sessvals.getUserID()));
return "edp/punching/editcases";
}
@RequestMapping(value = "caselist", method = RequestMethod.POST)
public String ListCases(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals,
@ModelAttribute(value = "caseGrid") CaseGrid caseGrid) {
CaseGrid CG = new CaseGrid();
CG.setErrCode("1002");
CG.setPortfolioId(caseGrid.getPortfolioId());
String[][] CaseList = CG.FillCaseList(caseGrid.getPortfolioId(), Sessvals.getBranchID());
if (CG.isProcessFlag()) {
model.addAttribute("caseList", CaseList);
model.addAttribute("caseGrid", CG);
} else {
caseGrid.setErrMsg(CG.getErrMsg());
model.addAttribute("caseList", CaseList);
model.addAttribute("caseGrid", caseGrid);
}
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("portlist", FillPortList(Sessvals.getBranchID(), Sessvals.getUserID()));
return "edp/punching/editcases";
}
public String[][] FillPortList(String BranchId, String userId) {
return new ModuleFunctions("1002").GetResultArray(249,
(BranchId + GlobalClass.ColDelim + userId + GlobalClass.ColDelim).split(GlobalClass.ColDelim));
}
}

View File

@@ -0,0 +1,128 @@
package matrix.nimble.edp.punching.controllers;
//servlet libraries
import java.net.URLDecoder;
import jakarta.servlet.http.HttpServletRequest;
//spring libraries
import matrix.nimble.edp.punching.model.PunchingHandler;
import matrix.nimble.model.Session;
import matrix.nimble.utilities.CommonFunctions;
import matrix.nimble.utilities.GlobalClass;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;
@Controller
@SessionAttributes({ "Sessvals" })
public class Punching {
@RequestMapping(value = "initdocview", method = RequestMethod.POST)
public String InitDocScreen(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals) {
PunchingHandler PH = new PunchingHandler();
PH.setErrCode("1901");
model.addAttribute("branchlist", null);
model.addAttribute("prodlist", null);
model.addAttribute("typelist", null);
model.addAttribute("doclist", null);
model.addAttribute("catlist", null);
model.addAttribute("portfolio_id", "-1");
model.addAttribute("Sessvals", Sessvals);
PH.GetDDValues(240, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist", PH.getOptionvals());
model.addAttribute("formmode", 0);
model.addAttribute("stat", true);
model.addAttribute("msg", "");
return "edp/punching/initdocs";
}
@RequestMapping(value = "docadd", method = RequestMethod.POST)
public String PunchDocs(ModelMap model, HttpServletRequest request,
@ModelAttribute(value = "Sessvals") Session Sessvals) {
String msg = "";
PunchingHandler PH = new PunchingHandler();
CommonFunctions comFunc = new CommonFunctions();
PH.setErrCode("1901");
PH.setPortfolioid(request.getParameter("portfolio_id"));
PH.setProcessFlag(true);
PH.DocDynamicHtml();
if (PH.isProcessFlag()) {
model.addAttribute("dynamichtml", PH.getDynamicpanel());
model.addAttribute("dynamicfields", PH.getDynamicfields());
model.addAttribute("docdynamichtml", PH.getDocdynamicpanel());
model.addAttribute("docdynamicfields", PH.getDocdynamicfields());
} else {
msg = PH.getErrMsg();
}
PH.GetDDValues(240, Sessvals.getBranchID().split(GlobalClass.ColDelim));
model.addAttribute("portlist", PH.getOptionvals());
PH.GetDDValues(4, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("branchlist", PH.getOptionvals());
PH.GetDDValues(5,
(Sessvals.getBranchID() + GlobalClass.ColDelim + "op.description='DOCUMENT' or op.description='APPTYPE'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if (PH.getOptionvals() != null) {
model.addAttribute("typelist", comFunc.GetSubArray("APPTYPE", 2, PH.getOptionvals()));
} else {
model.addAttribute("typelist", null);
}
PH.GetDDValues(241, PH.getPortfolioid().split(GlobalClass.ColDelim));
model.addAttribute("doclist", PH.getOptionvals());
PH.GetDDValues(70,
(PH.getPortfolioid() + GlobalClass.ColDelim + "op.description='CATEGORY' or op.description='PRODUCT'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
if (PH.getOptionvals() != null) {
model.addAttribute("prodlist", comFunc.GetSubArray("PRODUCT", 2, PH.getOptionvals()));
model.addAttribute("catlist", comFunc.GetSubArray("CATEGORY", 2, PH.getOptionvals()));
} else {
model.addAttribute("prodlist", null);
model.addAttribute("catlist", null);
}
model.addAttribute("portfolio_id", PH.getPortfolioid());
model.addAttribute("usid", Sessvals.getUserID());
model.addAttribute("Sessvals", Sessvals);
model.addAttribute("formmode", 1);
model.addAttribute("stat", PH.isProcessFlag());
model.addAttribute("msg", msg);
return "edp/punching/initdocs";
}
@RequestMapping(value = "addeditdoc", method = RequestMethod.POST)
public String AddEditDocs(@RequestParam String val0, @RequestParam String val1, @RequestParam String val2,
@RequestParam String val3, @RequestParam String val4, @RequestParam String val5, @RequestParam String val6,
@RequestParam String val7, @RequestParam String val8, @RequestParam String val9,
@RequestParam String val10) {
String retval = "success";
try {
// System.out.println(URLDecoder.decode(val0,"UTF-8"));
// System.out.println(URLDecoder.decode(val1,"UTF-8"));
// System.out.println(URLDecoder.decode(val2,"UTF-8"));
// System.out.println(URLDecoder.decode(val3,"UTF-8"));
// System.out.println(URLDecoder.decode(val4,"UTF-8"));
// System.out.println(URLDecoder.decode(val6,"UTF-8"));
// System.out.println(URLDecoder.decode(val7,"UTF-8"));
// System.out.println(URLDecoder.decode(val8,"UTF-8"));
// System.out.println(URLDecoder.decode(val9,"UTF-8"));
// System.out.println(URLDecoder.decode(val10,"UTF-8"));
PunchingHandler PH = new PunchingHandler();
PH.setErrCode("1902");
PH.setProcessFlag(true);
PH.AddEditDocs(val0, val1, val2, val3, val4, val6, val7, val8, val9, val10);
} catch (Exception exce) {
retval = exce.getMessage();
}
return retval;
}
}

View File

@@ -0,0 +1,87 @@
package matrix.nimble.edp.punching.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import jakarta.servlet.http.HttpSession;
import jakarta.servlet.http.HttpServletResponse;
import matrix.nimble.controller.AbstractAuthenticatedController;
import matrix.nimble.model.Session;
import matrix.services.commons.CommonService;
import matrix.services.edp.PunchingService;
import matrix.services.commons.CommonErrorService;
import lib.models.CasePunching;
@Controller
public class PunchingController extends AbstractAuthenticatedController {
private static final String INIT_VIEW_ROUTE = "initview";
private final PunchingService punchingService;
public PunchingController(CommonService commonService, CommonErrorService errorService,
PunchingService punchingService) {
super(commonService, errorService);
this.punchingService = punchingService;
}
@RequestMapping(value = INIT_VIEW_ROUTE, method = RequestMethod.POST)
public String InitializeScreen(
ModelMap model,
HttpSession httpSession,
HttpServletResponse response) {
PageAuthorization authorization = authorizePage(
INIT_VIEW_ROUTE, model, httpSession, response);
if (!authorization.isGranted()) {
return authorization.viewName();
}
Session Sessvals = authorization.session();
model.addAttribute("model", punchingService.init(
Sessvals.getBranchID(), Sessvals.getUserID(), null, "xxx"));
model.addAttribute("Sessvals", Sessvals);
return "edp/punching/initcase";
}
@RequestMapping(value = "caseadd", method = RequestMethod.POST)
public String addCase(
@ModelAttribute("model") CasePunching submitted,
ModelMap model,
HttpSession httpSession,
HttpServletResponse response) {
PageAuthorization authorization = authorizePage(
INIT_VIEW_ROUTE, model, httpSession, response);
if (!authorization.isGranted()) {
return authorization.viewName();
}
Session session = authorization.session();
model.addAttribute("model", punchingService.addCase(
submitted, session.getBranchID(), session.getUserID()));
model.addAttribute("Sessvals", session);
return "edp/punching/initcase";
}
@RequestMapping(value = "caseedit", method = RequestMethod.POST)
public String editCase(
@RequestParam("PortfolioId") Integer portfolioId,
@RequestParam("uuid") String documentCaseId,
ModelMap model,
HttpSession httpSession,
HttpServletResponse response) {
PageAuthorization authorization = authorizePage(
INIT_VIEW_ROUTE, model, httpSession, response);
if (!authorization.isGranted()) {
return authorization.viewName();
}
Session session = authorization.session();
model.addAttribute("model", punchingService.editCase(
portfolioId, documentCaseId, session.getBranchID(), session.getUserID()));
model.addAttribute("Sessvals", session);
return "edp/punching/initcase";
}
}

View File

@@ -0,0 +1,17 @@
package matrix.nimble.query;
import com.cygnus.db.QueryDefinition;
import com.cygnus.db.QueryDefinitionProvider;
public final class CloudQueryDefinitionProvider implements QueryDefinitionProvider {
private final QueryProvider provider;
public CloudQueryDefinitionProvider(QueryProvider provider) {
this.provider = provider;
}
@Override
public QueryDefinition get(int queryId) {
return QueryDefinition.parse(queryId, provider.getQuery(queryId));
}
}

View File

@@ -0,0 +1,34 @@
package matrix.nimble.query;
import com.cygnus.db.CygnusDbExecutor;
import com.cygnus.db.ExecutorOptions;
import com.cygnus.db.JdbcCygnusDbExecutor;
import matrix.nimble.utilities.DatabaseConnectionPool;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModernDbConfiguration implements DisposableBean {
private CygnusDbExecutor installed;
@Bean
CygnusDbExecutor cygnusDbExecutor(
QueryProvider queryProvider,
@Value("${CYGNUS_DB_FETCH_SIZE:250}") int fetchSize,
@Value("${CYGNUS_DB_QUERY_TIMEOUT_SECONDS:60}") int queryTimeoutSeconds) {
installed = new JdbcCygnusDbExecutor(
DatabaseConnectionPool.dataSource(),
new CloudQueryDefinitionProvider(queryProvider),
new ExecutorOptions(fetchSize, queryTimeoutSeconds));
ModernDbExecutors.install(installed);
return installed;
}
@Override
public void destroy() {
if (installed != null)
ModernDbExecutors.clear(installed);
}
}

View File

@@ -0,0 +1,24 @@
package matrix.nimble.query;
import com.cygnus.db.CygnusDbExecutor;
import java.util.concurrent.atomic.AtomicReference;
public final class ModernDbExecutors {
private static final AtomicReference<CygnusDbExecutor> CURRENT = new AtomicReference<>();
private ModernDbExecutors() {}
public static void install(CygnusDbExecutor executor) {
CURRENT.set(java.util.Objects.requireNonNull(executor, "executor"));
}
public static CygnusDbExecutor current() {
CygnusDbExecutor executor = CURRENT.get();
if (executor == null) throw new IllegalStateException("Modern database executor is not initialized");
return executor;
}
public static void clear(CygnusDbExecutor executor) {
CURRENT.compareAndSet(executor, null);
}
}

View File

@@ -7,7 +7,7 @@ import java.util.logging.Logger;
public final class RedisCachingQueryProvider implements QueryProvider {
public static final Duration QUERY_TTL = Duration.ofHours(12);
public static final Duration QUERY_TTL = Duration.ofHours(3);
private static final Logger LOGGER = Logger.getLogger(RedisCachingQueryProvider.class.getName());
private static final int LOCK_COUNT = 64;

View File

@@ -1,6 +1,14 @@
package matrix.nimble.utilities;
import java.sql.*;
import java.util.Hashtable;
import java.util.List;
import java.util.Optional;
import com.cygnus.db.DbResult;
import com.cygnus.db.RowMapper;
import com.cygnus.db.RowView;
import com.cygnus.db.RowConsumer;
import com.cygnus.db.TransactionCallback;
import matrix.nimble.query.ModernDbExecutors;
public class DBFunctions {
private Connection con;
@@ -294,7 +302,7 @@ public class DBFunctions {
}
return DataSet;
}
public String FetchRunQuery(int QueryCode,String Vals[])
public String FetchRunQuery(int QueryCode,String Vals[])
{
InitConfiguration();
setErrMsg("");
@@ -367,7 +375,60 @@ public class DBFunctions {
{
return getErrMsg();
}
}
}
public DbResult<RowView> execute(int queryId, Object[] parameters) {
return ModernDbExecutors.current().execute(queryId, parameters);
}
public <T> DbResult<T> execute(int queryId, Object[] parameters, Class<T> responseType) {
return ModernDbExecutors.current().execute(queryId, parameters, responseType);
}
public <T> List<T> query(int queryId, Object[] parameters, Class<T> responseType) {
return ModernDbExecutors.current().query(queryId, parameters, responseType);
}
public <T> List<T> query(int queryId, Object[] parameters, RowMapper<T> mapper) {
return ModernDbExecutors.current().query(queryId, parameters, mapper);
}
public <T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> responseType) {
return ModernDbExecutors.current().queryOne(queryId, parameters, responseType);
}
public int insert(int queryId, Object[] parameters) {
return ModernDbExecutors.current().insert(queryId, parameters);
}
public <K> K insert(int queryId, Object[] parameters, Class<K> generatedKeyType) {
return ModernDbExecutors.current().insert(queryId, parameters, generatedKeyType);
}
public int update(int queryId, Object[] parameters) {
return ModernDbExecutors.current().update(queryId, parameters);
}
public int delete(int queryId, Object[] parameters) {
return ModernDbExecutors.current().delete(queryId, parameters);
}
public <T> T procedure(int queryId, Object[] parameters, Class<T> responseType) {
return ModernDbExecutors.current().procedure(queryId, parameters, responseType);
}
public <T> List<T> procedureRows(int queryId, Object[] parameters, Class<T> responseType) {
return ModernDbExecutors.current().procedureRows(queryId, parameters, responseType);
}
public <T> void stream(
int queryId, Object[] parameters, RowMapper<T> mapper, RowConsumer<T> consumer) {
ModernDbExecutors.current().stream(queryId, parameters, mapper, consumer);
}
public <T> T transaction(TransactionCallback<T> callback) {
return ModernDbExecutors.current().transaction(callback);
}
@SuppressWarnings("finally")
public String QueryBuilder(String Query,String[] Val)
{

View File

@@ -5,6 +5,7 @@ import java.sql.SQLException;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
/** Shared, bounded JDBC connection pool for the legacy database layer. */
public final class DatabaseConnectionPool {
@@ -20,6 +21,10 @@ public final class DatabaseConnectionPool {
return getDataSource().getConnection();
}
public static DataSource dataSource() {
return getDataSource();
}
private static HikariDataSource getDataSource() {
HikariDataSource current = dataSource;
if (current != null) {

View File

@@ -0,0 +1,33 @@
package matrix.services.commons;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lib.models.ErrorDetails;
import matrix.nimble.model.Session;
import org.springframework.stereotype.Service;
import org.springframework.ui.ModelMap;
@Service
public class CommonErrorService {
public static final String ERROR_ATTRIBUTE = "errorDetails";
private final CommonService commonService;
public CommonErrorService(CommonService commonService) {
this.commonService = commonService;
}
public String render(
ModelMap model,
HttpServletResponse response,
HttpSession httpSession,
ErrorDetails errorDetails) {
response.setStatus(errorDetails.getHttpStatus());
model.addAttribute(ERROR_ATTRIBUTE, errorDetails);
Session session = commonService.getSession(httpSession);
if (session != null) {
model.addAttribute(CommonService.SESSION_ATTRIBUTE, session);
}
return "error";
}
}

View File

@@ -0,0 +1,102 @@
package matrix.services.commons;
import com.cygnus.db.CygnusDbExecutor;
import jakarta.servlet.http.HttpSession;
import lib.models.Option;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import matrix.nimble.model.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class CommonService {
public static final String SESSION_ATTRIBUTE = "Sessvals";
private static final Pattern MENU_COMMAND = Pattern.compile(
"SubmitMenuCommand\\(\\s*'([^']+)'\\s*,",
Pattern.CASE_INSENSITIVE);
private final CygnusDbExecutor dbExecutor;
@Autowired
public CommonService(CygnusDbExecutor dbExecutor) {
this.dbExecutor = Objects.requireNonNull(dbExecutor, "dbExecutor");
}
/* Used by the session-only unit tests. Production construction is handled
by Spring through the CygnusDbExecutor constructor above. */
public CommonService() {
this.dbExecutor = null;
}
public Session getSession(HttpSession httpSession) {
if (httpSession == null) {
return null;
}
Object value = httpSession.getAttribute(SESSION_ATTRIBUTE);
return value instanceof Session session ? session : null;
}
public void storeSession(HttpSession httpSession, Session session) {
Objects.requireNonNull(httpSession, "httpSession");
httpSession.setAttribute(SESSION_ATTRIBUTE, Objects.requireNonNull(session, "session"));
}
public boolean hasPageAccess(HttpSession httpSession, String pageRoute) {
return hasPageAccess(getSession(httpSession), pageRoute);
}
public boolean hasPageAccess(Session session, String pageRoute) {
if (session == null || session.getMenuHtml() == null) {
return false;
}
String requiredRoute = normalizeRoute(pageRoute);
if (requiredRoute.isEmpty()) {
return false;
}
Matcher matcher = MENU_COMMAND.matcher(session.getMenuHtml());
while (matcher.find()) {
if (normalizeRoute(matcher.group(1)).equals(requiredRoute)) {
return true;
}
}
return false;
}
private String normalizeRoute(String route) {
if (route == null) {
return "";
}
String normalized = route.trim().replace('\\', '/');
int query = normalized.indexOf('?');
if (query >= 0) {
normalized = normalized.substring(0, query);
}
int fragment = normalized.indexOf('#');
if (fragment >= 0) {
normalized = normalized.substring(0, fragment);
}
while (normalized.endsWith("/")) {
normalized = normalized.substring(0, normalized.length() - 1);
}
int slash = normalized.lastIndexOf('/');
return slash >= 0 ? normalized.substring(slash + 1) : normalized;
}
public List<Option> getOptions(Integer queryId, Object[] queryParams) {
if (queryId == null) {
return List.of();
}
if (dbExecutor == null) {
throw new IllegalStateException("CygnusDbExecutor is not configured");
}
Object[] parameters = queryParams == null ? new Object[0] : queryParams;
return dbExecutor.query(queryId, parameters, Option.class);
}
}

View File

@@ -0,0 +1,195 @@
package matrix.services.edp;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.springframework.stereotype.Service;
import lib.models.CasePunching;
import lib.models.ErrorDetails;
import lib.models.Option;
import lib.constants.ApplicationError;
import matrix.nimble.edp.punching.model.PunchingHandler;
import matrix.nimble.utilities.CommonFunctions;
import matrix.nimble.utilities.GlobalClass;
import matrix.services.commons.CommonService;
@Service
public class PunchingService {
private static final String PORTFOLIO = "portfolio.";
private static final String BRANCH = "branch.";
private static final String PRODUCT = "product.";
private static final String CITY = "city.";
private static final String APPLICATION_TYPE = "applicationType.";
private static final String CATEGORY = "category.";
private final CommonService commonService;
public PunchingService(CommonService commonService) {
this.commonService = commonService;
}
public CasePunching init(
String branchId,
String userId,
String verificationCaseId,
String documentCaseId) {
PunchingHandler punchingHandler = new PunchingHandler();
punchingHandler.setErrCode("1001");
CasePunching casePunching = new CasePunching();
Map<String, List<Option>> options = new LinkedHashMap<>();
Short databaseBranchId = Short.valueOf(branchId.trim());
List<Option> portfolios = commonService.getOptions(
3, new Object[] { databaseBranchId, Short.valueOf((short) 1) });
addOptions(options, PORTFOLIO, portfolios);
casePunching.setOptions(Collections.unmodifiableMap(options));
casePunching.setVisibleSections(List.of());
casePunching.setPortfolioId(-1);
casePunching.setFormMode(0);
casePunching.setUserId(userId);
casePunching.setDynamicFields("");
casePunching.setDynamicHtml("");
casePunching.setVerificationCaseId(verificationCaseId);
casePunching.setDocumentCaseId(documentCaseId);
return casePunching;
}
public CasePunching addCase(
CasePunching submitted,
String branchIds,
String userId) {
CasePunching result = new CasePunching();
result.setPortfolioId(submitted.getPortfolioId());
result.setFormMode(1);
result.setUserId(userId);
result.setVerificationCaseId(submitted.getVerificationCaseId());
result.setDocumentCaseId("xxx");
if (submitted.getPortfolioId() == null || submitted.getPortfolioId() <= 0) {
result.setOptions(Collections.emptyMap());
result.setVisibleSections(List.of());
result.setDynamicFields("");
result.setDynamicHtml("");
result.setErrorDetails(new ErrorDetails(ApplicationError.BAD_REQUEST));
return result;
}
String portfolioId = submitted.getPortfolioId().toString();
PunchingHandler punchingHandler = new PunchingHandler();
CommonFunctions commonFunctions = new CommonFunctions();
punchingHandler.setErrCode("1001");
punchingHandler.setPortfolioid(portfolioId);
punchingHandler.setProcessFlag(true);
punchingHandler.DynamicHtml("10");
if (punchingHandler.isProcessFlag()) {
result.setDynamicFields(defaultString(punchingHandler.getDynamicfields()));
result.setDynamicHtml(defaultString(punchingHandler.getDynamicpanel()));
String sections = defaultString(punchingHandler.GetVisibleSections())
+ defaultString(punchingHandler.getDynamicsection());
result.setVisibleSections(toVisibleSections(sections));
} else {
result.setDynamicFields("");
result.setDynamicHtml("");
result.setVisibleSections(List.of());
result.setErrorDetails(new ErrorDetails(ApplicationError.INTERNAL_SERVER_ERROR));
}
Map<String, String> options = new LinkedHashMap<>();
punchingHandler.GetDDValues(3, branchIds.split(GlobalClass.ColDelim));
addOptions(options, PORTFOLIO, punchingHandler.getOptionvals());
punchingHandler.GetDDValues(4, new String[] { portfolioId });
addOptions(options, BRANCH, punchingHandler.getOptionvals());
punchingHandler.GetDDValues(5,
(branchIds + GlobalClass.ColDelim
+ "op.description='CITY' or op.description='APPTYPE'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
String[][] generalOptions = punchingHandler.getOptionvals();
if (generalOptions != null) {
addOptions(options, CITY,
commonFunctions.GetSubArray("CITY", 2, generalOptions));
addOptions(options, APPLICATION_TYPE,
commonFunctions.GetSubArray("APPTYPE", 2, generalOptions));
}
punchingHandler.GetDDValues(70,
(portfolioId + GlobalClass.ColDelim
+ "op.description='CATEGORY' or op.description='PRODUCT'"
+ GlobalClass.ColDelim).split(GlobalClass.ColDelim));
String[][] portfolioOptions = punchingHandler.getOptionvals();
if (portfolioOptions != null) {
addOptions(options, PRODUCT,
commonFunctions.GetSubArray("PRODUCT", 2, portfolioOptions));
addOptions(options, CATEGORY,
commonFunctions.GetSubArray("CATEGORY", 2, portfolioOptions));
}
result.setOptionsl(Collections.unmodifiableMap(options));
return result;
}
public CasePunching editCase(
Integer portfolioId,
String documentCaseId,
String branchIds,
String userId) {
CasePunching submitted = new CasePunching();
submitted.setPortfolioId(portfolioId);
submitted.setDocumentCaseId(documentCaseId);
CasePunching result = addCase(submitted, branchIds, userId);
result.setFormMode(2);
result.setDocumentCaseId(documentCaseId);
return result;
}
private void addOptions(Map<String, String> options, String prefix, String[][] values) {
if (values == null) {
return;
}
for (String[] value : values) {
if (value != null && value.length >= 2 && value[0] != null) {
options.put(prefix + value[0], value[1] == null ? "" : value[1]);
}
}
}
private void addOptions(Map<String, List<Option>> options, String prefix, List<Option> values) {
if (values == null || values.isEmpty()) {
return;
}
List<Option> groupOptions = options.get(prefix);
for (Option value : values) {
if (value == null || value.getValue() == null) {
continue;
}
if (groupOptions == null) {
groupOptions = new java.util.ArrayList<>(values.size());
options.put(prefix, groupOptions);
}
groupOptions.add(value);
}
}
private List<String> toVisibleSections(String sections) {
if (sections == null || sections.isBlank()) {
return List.of();
}
return List.of(sections.split(Pattern.quote(GlobalClass.ColDelim))).stream()
.map(String::trim)
.filter(section -> !section.isEmpty() && !"null".equalsIgnoreCase(section))
.distinct()
.toList();
}
private String defaultString(String value) {
return value == null ? "" : value;
}
}

View File

@@ -0,0 +1,70 @@
package matrix.nimble.controller;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import matrix.nimble.model.Session;
import matrix.services.commons.CommonErrorService;
import matrix.services.commons.CommonService;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.ui.ModelMap;
class AbstractAuthenticatedControllerTest {
private final CommonService commonService = new CommonService();
private final TestController controller = new TestController(
commonService, new CommonErrorService(commonService));
@Test
void grantsAConfiguredMenuRoute() {
MockHttpSession httpSession = new MockHttpSession();
Session session = sessionWithRoute("initview");
commonService.storeSession(httpSession, session);
AbstractAuthenticatedController.PageAuthorization result = controller.authorize(
"initview", new ModelMap(), httpSession, new MockHttpServletResponse());
assertTrue(result.isGranted());
assertSame(session, result.session());
}
@Test
void returns401ForMissingSessionAnd403ForMissingPage() {
MockHttpServletResponse noSessionResponse = new MockHttpServletResponse();
AbstractAuthenticatedController.PageAuthorization noSession = controller.authorize(
"initview", new ModelMap(), new MockHttpSession(), noSessionResponse);
assertEquals("error", noSession.viewName());
assertEquals(401, noSessionResponse.getStatus());
MockHttpSession httpSession = new MockHttpSession();
commonService.storeSession(httpSession, sessionWithRoute("dashboard"));
MockHttpServletResponse deniedResponse = new MockHttpServletResponse();
AbstractAuthenticatedController.PageAuthorization denied = controller.authorize(
"initview", new ModelMap(), httpSession, deniedResponse);
assertEquals("error", denied.viewName());
assertEquals(403, deniedResponse.getStatus());
}
private Session sessionWithRoute(String route) {
Session session = new Session();
session.setMenuHtml("<a onclick=\"SubmitMenuCommand('" + route
+ "','_parent','MenuForm','')\">Page</a>");
return session;
}
private static final class TestController extends AbstractAuthenticatedController {
private TestController(CommonService commonService, CommonErrorService errorService) {
super(commonService, errorService);
}
private PageAuthorization authorize(
String route,
ModelMap model,
MockHttpSession session,
MockHttpServletResponse response) {
return authorizePage(route, model, session, response);
}
}
}

View File

@@ -0,0 +1,28 @@
package matrix.nimble.query;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import com.cygnus.db.QueryDefinitionException;
import com.cygnus.db.QueryType;
import org.junit.jupiter.api.Test;
class CloudQueryDefinitionProviderTest {
@Test
void adaptsEncryptedCloudQueryProviderValueToDbDefinition() {
CloudQueryDefinitionProvider provider = new CloudQueryDefinitionProvider(
queryId -> "select!C0L!select operation_id from operation where type = ?");
var definition = provider.get(513);
assertEquals(513, definition.queryId());
assertEquals(QueryType.SELECT, definition.type());
assertEquals("select operation_id from operation where type = ?", definition.sql());
}
@Test
void preventsLegacyPlaceholderExecution() {
CloudQueryDefinitionProvider provider = new CloudQueryDefinitionProvider(
queryId -> "select!C0L!select * from operation where type = 'val0'");
assertThrows(QueryDefinitionException.class, () -> provider.get(513));
}
}

View File

@@ -0,0 +1,35 @@
package matrix.services.commons;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import lib.models.ErrorDetails;
import lib.constants.ApplicationError;
import matrix.nimble.model.Session;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.ui.ModelMap;
class CommonErrorServiceTest {
private final CommonService commonService = new CommonService();
private final CommonErrorService errorService = new CommonErrorService(commonService);
@Test
void appliesHttpStatusAndPreservesAnActiveSessionForTheMenu() {
MockHttpSession httpSession = new MockHttpSession();
Session session = new Session();
session.setMenuHtml("<div id='MenuBar'></div>");
commonService.storeSession(httpSession, session);
ErrorDetails details = new ErrorDetails(ApplicationError.ACCESS_DENIED);
MockHttpServletResponse response = new MockHttpServletResponse();
ModelMap model = new ModelMap();
String view = errorService.render(model, response, httpSession, details);
assertEquals("error", view);
assertEquals(403, response.getStatus());
assertSame(details, model.get(CommonErrorService.ERROR_ATTRIBUTE));
assertSame(session, model.get(CommonService.SESSION_ATTRIBUTE));
}
}

View File

@@ -0,0 +1,45 @@
package matrix.services.commons;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import matrix.nimble.model.Session;
import org.junit.jupiter.api.Test;
import org.springframework.mock.web.MockHttpSession;
class CommonServiceTest {
private final CommonService service = new CommonService();
@Test
void storesAndReadsTheAuthenticatedSession() {
MockHttpSession httpSession = new MockHttpSession();
Session session = sessionWithMenu("initview");
service.storeSession(httpSession, session);
assertSame(session, service.getSession(httpSession));
}
@Test
void grantsOnlyAnExactMenuCommandRoute() {
Session session = sessionWithMenu("/matrix/ver/initview?source=menu");
assertTrue(service.hasPageAccess(session, "initview"));
assertFalse(service.hasPageAccess(session, "view"));
assertFalse(service.hasPageAccess(session, "initview-admin"));
}
@Test
void deniesMissingSessionAndMissingMenu() {
assertFalse(service.hasPageAccess((Session) null, "initview"));
assertFalse(service.hasPageAccess(new Session(), "initview"));
}
private Session sessionWithMenu(String route) {
Session session = new Session();
session.setMenuHtml("<a onclick=\"SubmitMenuCommand('" + route
+ "','_parent','MenuForm','')\">Punching</a>");
return session;
}
}

View File

@@ -0,0 +1,61 @@
# Cygnus On-Premises Database Core
This module executes cloud-supplied, parameterized query definitions against the
on-premises JDBC `DataSource` without creating delimiter strings or `String[][]`
result buffers.
## Query format
The query catalog value retains the operation prefix during incremental migration:
```text
select!C0L!select id, name from operation where operation_type = ? and operated_by = ?
```
Legacy `val0`, `val1`, ... placeholders are rejected by the modern executor. They
remain available through the existing `DBFunctions.FetchRunQuery` path until each
query is migrated.
## Usage
```java
List<Operation> rows = dbFunctions.query(
600,
new Object[] { operationType, operatedBy },
Operation.class);
```
For maximum throughput, use an explicit mapper:
```java
List<Operation> rows = dbFunctions.query(
600,
parameters,
result -> new Operation(result.getLong("id"), result.getString("name")));
```
Large reports can use `stream(...)` so rows are consumed without retaining the
whole result in memory. PostgreSQL cursor fetching is enabled by running reads with
auto-commit disabled and the configured fetch size.
## Dynamic multi-value parameters
Use a typed array parameter with PostgreSQL `ANY` when the number of values is
not known in advance. The cached query remains parameterized:
```sql
select ... where op.description = any(?)
```
```java
dbExecutor.query(
queryId,
new Object[] { portfolioId, SqlArrayParameter.text(descriptions) },
Option.class);
```
Factories are available for text, integer, smallint, bigint, numeric, boolean,
UUID, date, timestamp and timestamp-with-time-zone arrays. Use
`SqlArrayParameter.of(postgresType, values)` for another PostgreSQL scalar or
enum type. An empty collection is valid and causes `= ANY(empty_array)` to
match no rows.

38
cygnus-onprem-db/pom.xml Normal file
View File

@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.cygnus</groupId>
<artifactId>cygnus-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>cygnus-onprem-db</artifactId>
<packaging>jar</packaging>
<name>Cygnus On-Premises Database Core</name>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.3.232</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,21 @@
package com.cygnus.db;
import java.util.List;
import java.util.Optional;
public interface CygnusDbExecutor {
<T> DbResult<T> execute(int queryId, Object[] parameters, Class<T> responseType);
DbResult<RowView> execute(int queryId, Object[] parameters);
<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);
<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);
int update(int queryId, Object[] parameters);
int delete(int queryId, Object[] parameters);
<T> T procedure(int queryId, Object[] parameters, Class<T> responseType);
<T> List<T> procedureRows(int queryId, Object[] parameters, Class<T> responseType);
<T> void stream(int queryId, Object[] parameters, RowMapper<T> mapper, RowConsumer<T> consumer);
<T> T transaction(TransactionCallback<T> callback);
}

View File

@@ -0,0 +1,12 @@
package com.cygnus.db;
public final class DatabaseExecutionException extends RuntimeException {
private final int queryId;
public DatabaseExecutionException(int queryId, String message, Throwable cause) {
super(message, cause);
this.queryId = queryId;
}
public int queryId() { return queryId; }
}

View File

@@ -0,0 +1,20 @@
package com.cygnus.db;
import java.util.List;
import java.util.Optional;
public record DbResult<T>(QueryType type, List<T> rows, int affectedRows, T value) {
public DbResult {
rows = rows == null ? List.of() : List.copyOf(rows);
}
public Optional<T> optionalValue() { return Optional.ofNullable(value); }
public static <T> DbResult<T> rows(QueryType type, List<T> rows) {
return new DbResult<>(type, rows, 0, rows.isEmpty() ? null : rows.getFirst());
}
public static <T> DbResult<T> affected(QueryType type, int count) {
return new DbResult<>(type, List.of(), count, null);
}
}

View File

@@ -0,0 +1,10 @@
package com.cygnus.db;
public record ExecutorOptions(int fetchSize, int queryTimeoutSeconds) {
public static final ExecutorOptions DEFAULT = new ExecutorOptions(250, 60);
public ExecutorOptions {
if (fetchSize < 0) throw new IllegalArgumentException("fetchSize cannot be negative");
if (queryTimeoutSeconds < 0) throw new IllegalArgumentException("queryTimeoutSeconds cannot be negative");
}
}

View File

@@ -0,0 +1,33 @@
package com.cygnus.db;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
final class ImmutableRowView implements RowView {
private final Map<String, Object> values;
private final Map<String, Object> normalized;
ImmutableRowView(Map<String, Object> values) {
this.values = Collections.unmodifiableMap(new LinkedHashMap<>(values));
Map<String, Object> index = new LinkedHashMap<>();
values.forEach((name, value) -> index.put(name.toLowerCase(Locale.ROOT), value));
this.normalized = Map.copyOf(index);
}
@Override
public Object get(String column) {
Object value = values.get(column);
return value != null || values.containsKey(column)
? value : normalized.get(column.toLowerCase(Locale.ROOT));
}
@Override
public <T> T get(String column, Class<T> type) {
return ValueConverter.convert(get(column), type);
}
@Override
public Map<String, Object> asMap() { return values; }
}

View File

@@ -0,0 +1,316 @@
package com.cygnus.db;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.sql.DataSource;
public final class JdbcCygnusDbExecutor implements CygnusDbExecutor {
private final DataSource dataSource;
private final QueryDefinitionProvider queryProvider;
private final ExecutorOptions options;
private final ParameterBinder binder = new ParameterBinder();
private final ObjectRowMapperFactory mapperFactory = new ObjectRowMapperFactory();
public JdbcCygnusDbExecutor(DataSource dataSource, QueryDefinitionProvider queryProvider) {
this(dataSource, queryProvider, ExecutorOptions.DEFAULT);
}
public JdbcCygnusDbExecutor(
DataSource dataSource, QueryDefinitionProvider queryProvider, ExecutorOptions options) {
this.dataSource = java.util.Objects.requireNonNull(dataSource, "dataSource");
this.queryProvider = java.util.Objects.requireNonNull(queryProvider, "queryProvider");
this.options = java.util.Objects.requireNonNull(options, "options");
}
@Override
public <T> DbResult<T> execute(int queryId, Object[] parameters, Class<T> responseType) {
QueryDefinition definition = queryProvider.get(queryId);
if (definition.type() == QueryType.SELECT) {
return withReadConnection(queryId, connection ->
executeRowsOrCount(connection, definition, parameters, responseType));
}
if (definition.type() == QueryType.PROCEDURE) {
return executeRowsOrCount(definition, parameters, responseType);
}
return DbResult.affected(definition.type(), executeUpdate(definition, parameters));
}
@Override
public DbResult<RowView> execute(int queryId, Object[] parameters) {
return execute(queryId, parameters, RowView.class);
}
@Override
public <T> List<T> query(int queryId, Object[] parameters, Class<T> responseType) {
return withReadConnection(queryId, connection -> query(
connection, require(queryId, QueryType.SELECT), parameters, responseType));
}
@Override
public <T> List<T> query(int queryId, Object[] parameters, RowMapper<T> mapper) {
return withReadConnection(queryId, connection -> query(
connection, require(queryId, QueryType.SELECT), parameters, mapper));
}
@Override
public List<RowView> query(int queryId, Object[] parameters) {
return query(queryId, parameters, RowView.class);
}
@Override
public <T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> responseType) {
List<T> rows = query(queryId, parameters, responseType);
if (rows.size() > 1) throw new DatabaseExecutionException(
queryId, "Expected one row but received " + rows.size(), null);
return rows.stream().findFirst();
}
@Override
public int insert(int queryId, Object[] parameters) {
return executeUpdate(require(queryId, QueryType.INSERT), parameters);
}
@Override
public <K> K insert(int queryId, Object[] parameters, Class<K> generatedKeyType) {
QueryDefinition definition = require(queryId, QueryType.INSERT);
return withConnection(queryId, connection -> {
try (PreparedStatement statement = prepare(
connection, definition, Statement.RETURN_GENERATED_KEYS)) {
binder.bind(statement, parameters);
statement.executeUpdate();
try (ResultSet keys = statement.getGeneratedKeys()) {
if (!keys.next()) throw new DatabaseExecutionException(
queryId, "Insert did not return a generated key", null);
return ValueConverter.convert(keys.getObject(1), generatedKeyType);
}
}
});
}
@Override
public int update(int queryId, Object[] parameters) {
return executeUpdate(require(queryId, QueryType.UPDATE), parameters);
}
@Override
public int delete(int queryId, Object[] parameters) {
return executeUpdate(require(queryId, QueryType.DELETE), parameters);
}
@Override
public <T> T procedure(int queryId, Object[] parameters, Class<T> responseType) {
List<T> rows = procedureRows(queryId, parameters, responseType);
return rows.isEmpty() ? null : rows.getFirst();
}
@Override
public <T> List<T> procedureRows(int queryId, Object[] parameters, Class<T> responseType) {
DbResult<T> result = executeRowsOrCount(
require(queryId, QueryType.PROCEDURE), parameters, responseType);
return result.rows();
}
@Override
public <T> void stream(
int queryId, Object[] parameters, RowMapper<T> mapper, RowConsumer<T> consumer) {
QueryDefinition definition = require(queryId, QueryType.SELECT);
withReadConnection(queryId, connection -> {
try (PreparedStatement statement = prepare(connection, definition);
ResultSet result = executeQuery(statement, parameters)) {
while (result.next()) consumer.accept(mapper.map(result));
return null;
}
});
}
@Override
public <T> T transaction(TransactionCallback<T> callback) {
try (Connection connection = dataSource.getConnection()) {
boolean autoCommit = connection.getAutoCommit();
connection.setAutoCommit(false);
try {
T result = callback.execute(new TransactionOperationsImpl(connection));
connection.commit();
return result;
} catch (Exception exception) {
try { connection.rollback(); } catch (SQLException rollback) { exception.addSuppressed(rollback); }
if (exception instanceof RuntimeException runtime) throw runtime;
throw new DatabaseExecutionException(0, "Transaction failed", exception);
} finally {
connection.setAutoCommit(autoCommit);
}
} catch (SQLException exception) {
throw new DatabaseExecutionException(0, "Unable to manage transaction", exception);
}
}
private <T> DbResult<T> executeRowsOrCount(
QueryDefinition definition, Object[] parameters, Class<T> responseType) {
return withConnection(definition.queryId(), connection ->
executeRowsOrCount(connection, definition, parameters, responseType));
}
private <T> DbResult<T> executeRowsOrCount(
Connection connection,
QueryDefinition definition,
Object[] parameters,
Class<T> responseType) throws SQLException {
try (PreparedStatement statement = prepare(connection, definition)) {
binder.bind(statement, parameters);
boolean rows = statement.execute();
if (!rows) return DbResult.affected(definition.type(), statement.getUpdateCount());
try (ResultSet result = statement.getResultSet()) {
return DbResult.rows(definition.type(), map(result, responseType));
}
}
}
private int executeUpdate(QueryDefinition definition, Object[] parameters) {
return withConnection(definition.queryId(), connection ->
executeUpdate(connection, definition, parameters));
}
private int executeUpdate(
Connection connection, QueryDefinition definition, Object[] parameters) throws SQLException {
try (PreparedStatement statement = prepare(connection, definition)) {
binder.bind(statement, parameters);
return statement.executeUpdate();
}
}
private <T> List<T> query(
Connection connection,
QueryDefinition definition,
Object[] parameters,
Class<T> responseType) throws SQLException {
try (PreparedStatement statement = prepare(connection, definition);
ResultSet result = executeQuery(statement, parameters)) {
return map(result, responseType);
}
}
private <T> List<T> query(
Connection connection,
QueryDefinition definition,
Object[] parameters,
RowMapper<T> mapper) throws SQLException {
try (PreparedStatement statement = prepare(connection, definition);
ResultSet result = executeQuery(statement, parameters)) {
List<T> rows = new ArrayList<>();
while (result.next()) rows.add(mapper.map(result));
return List.copyOf(rows);
}
}
private <T> List<T> map(ResultSet result, Class<T> responseType) throws SQLException {
RowMapper<T> mapper = mapperFactory.create(responseType, result.getMetaData());
List<T> rows = new ArrayList<>();
while (result.next()) rows.add(mapper.map(result));
return List.copyOf(rows);
}
private ResultSet executeQuery(PreparedStatement statement, Object[] parameters) throws SQLException {
binder.bind(statement, parameters);
return statement.executeQuery();
}
private PreparedStatement prepare(Connection connection, QueryDefinition definition) throws SQLException {
PreparedStatement statement = connection.prepareStatement(
definition.sql(), ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
configure(statement);
return statement;
}
private PreparedStatement prepare(
Connection connection, QueryDefinition definition, int generatedKeys) throws SQLException {
PreparedStatement statement = connection.prepareStatement(definition.sql(), generatedKeys);
configure(statement);
return statement;
}
private void configure(PreparedStatement statement) throws SQLException {
statement.setFetchSize(options.fetchSize());
statement.setQueryTimeout(options.queryTimeoutSeconds());
}
private QueryDefinition require(int queryId, QueryType expected) {
QueryDefinition definition = queryProvider.get(queryId);
if (definition.type() != expected) throw new QueryDefinitionException(
"Query " + queryId + " is " + definition.type() + ", not " + expected);
return definition;
}
private <T> T withConnection(int queryId, SqlWork<T> work) {
try (Connection connection = dataSource.getConnection()) {
return work.execute(connection);
} catch (DatabaseExecutionException exception) {
throw exception;
} catch (Exception exception) {
throw new DatabaseExecutionException(queryId, "Database operation failed for query " + queryId, exception);
}
}
private <T> T withReadConnection(int queryId, SqlWork<T> work) {
try (Connection connection = dataSource.getConnection()) {
boolean autoCommit = connection.getAutoCommit();
connection.setAutoCommit(false); // PostgreSQL requires this for cursor-based fetchSize.
try {
T value = work.execute(connection);
connection.commit();
return value;
} catch (Exception exception) {
try { connection.rollback(); } catch (SQLException rollback) { exception.addSuppressed(rollback); }
if (exception instanceof RuntimeException runtime) throw runtime;
throw new DatabaseExecutionException(queryId,
"Database read failed for query " + queryId, exception);
} finally {
connection.setAutoCommit(autoCommit);
}
} catch (SQLException exception) {
throw new DatabaseExecutionException(queryId,
"Unable to manage database read for query " + queryId, exception);
}
}
@FunctionalInterface
private interface SqlWork<T> { T execute(Connection connection) throws Exception; }
private final class TransactionOperationsImpl implements TransactionOperations {
private final Connection connection;
private TransactionOperationsImpl(Connection connection) { this.connection = connection; }
@Override
public <T> List<T> query(int queryId, Object[] parameters, Class<T> type) {
try { return JdbcCygnusDbExecutor.this.query(
connection, require(queryId, QueryType.SELECT), parameters, type); }
catch (SQLException exception) { throw failure(queryId, exception); }
}
@Override
public <T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> type) {
List<T> rows = query(queryId, parameters, type);
if (rows.size() > 1) throw new DatabaseExecutionException(
queryId, "Expected one row but received " + rows.size(), null);
return rows.stream().findFirst();
}
@Override public int insert(int id, Object[] values) { return updateType(id, values, QueryType.INSERT); }
@Override public int update(int id, Object[] values) { return updateType(id, values, QueryType.UPDATE); }
@Override public int delete(int id, Object[] values) { return updateType(id, values, QueryType.DELETE); }
private int updateType(int id, Object[] values, QueryType type) {
try { return executeUpdate(connection, require(id, type), values); }
catch (SQLException exception) { throw failure(id, exception); }
}
private DatabaseExecutionException failure(int id, SQLException exception) {
return new DatabaseExecutionException(id, "Transaction operation failed for query " + id, exception);
}
}
}

View File

@@ -0,0 +1,141 @@
package com.cygnus.db;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.RecordComponent;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
final class ObjectRowMapperFactory {
private final ConcurrentMap<Class<?>, TypeMetadata> metadataCache = new ConcurrentHashMap<>();
<T> RowMapper<T> create(Class<T> type, ResultSetMetaData resultMetadata) throws SQLException {
if (type == RowView.class) return result -> type.cast(rowView(result, resultMetadata));
if (isScalar(type)) return result -> ValueConverter.convert(result.getObject(1), type);
TypeMetadata typeMetadata = metadataCache.computeIfAbsent(type, this::inspect);
Map<String, Integer> columns = columns(resultMetadata);
return typeMetadata.mapper(type, columns);
}
private RowView rowView(java.sql.ResultSet result, ResultSetMetaData metadata) throws SQLException {
Map<String, Object> values = new LinkedHashMap<>();
for (int column = 1; column <= metadata.getColumnCount(); column++) {
values.put(metadata.getColumnLabel(column), result.getObject(column));
}
return new ImmutableRowView(values);
}
private TypeMetadata inspect(Class<?> type) {
try {
if (type.isRecord()) {
RecordComponent[] components = type.getRecordComponents();
Class<?>[] parameterTypes = new Class<?>[components.length];
String[] names = new String[components.length];
for (int index = 0; index < components.length; index++) {
parameterTypes[index] = components[index].getType();
names[index] = normalize(components[index].getName());
}
Constructor<?> constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
return new RecordMetadata(constructor, parameterTypes, names);
}
Constructor<?> constructor = type.getDeclaredConstructor();
constructor.setAccessible(true);
List<Field> fields = new ArrayList<>();
for (Class<?> current = type; current != null && current != Object.class;
current = current.getSuperclass()) {
for (Field field : current.getDeclaredFields()) {
if (!java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
fields.add(field);
}
}
}
return new BeanMetadata(constructor, List.copyOf(fields));
} catch (ReflectiveOperationException exception) {
throw new IllegalArgumentException(
"Response type must be a record or have a no-argument constructor: " + type.getName(), exception);
}
}
private Map<String, Integer> columns(ResultSetMetaData metadata) throws SQLException {
Map<String, Integer> result = new LinkedHashMap<>();
for (int column = 1; column <= metadata.getColumnCount(); column++) {
result.putIfAbsent(normalize(metadata.getColumnLabel(column)), column);
}
return result;
}
private static String normalize(String value) {
return value.replace("_", "").toLowerCase(Locale.ROOT);
}
private static boolean isScalar(Class<?> type) {
return type.isPrimitive() || type.isEnum()
|| Number.class.isAssignableFrom(type)
|| type == String.class || type == Boolean.class || type == Character.class
|| type == java.util.UUID.class
|| type.getPackageName().equals("java.time");
}
private sealed interface TypeMetadata permits RecordMetadata, BeanMetadata {
<T> RowMapper<T> mapper(Class<T> type, Map<String, Integer> columns);
}
private record RecordMetadata(
Constructor<?> constructor, Class<?>[] parameterTypes, String[] names) implements TypeMetadata {
@Override
public <T> RowMapper<T> mapper(Class<T> type, Map<String, Integer> columns) {
int[] indexes = new int[names.length];
for (int index = 0; index < names.length; index++) {
Integer column = columns.get(names[index]);
if (column == null) throw new IllegalArgumentException(
"Result does not contain record component: " + names[index]);
indexes[index] = column;
}
return result -> {
try {
Object[] values = new Object[indexes.length];
for (int index = 0; index < indexes.length; index++) {
values[index] = ValueConverter.convert(
result.getObject(indexes[index]), parameterTypes[index]);
}
return type.cast(constructor.newInstance(values));
} catch (ReflectiveOperationException exception) {
throw new DatabaseExecutionException(0, "Unable to map record " + type.getName(), exception);
}
};
}
}
private record BeanMetadata(Constructor<?> constructor, List<Field> fields) implements TypeMetadata {
@Override
public <T> RowMapper<T> mapper(Class<T> type, Map<String, Integer> columns) {
List<FieldBinding> bindings = fields.stream()
.map(field -> new FieldBinding(field, columns.get(normalize(field.getName()))))
.filter(binding -> binding.column() != null)
.toList();
return result -> {
try {
T instance = type.cast(constructor.newInstance());
for (FieldBinding binding : bindings) {
binding.field().set(instance, ValueConverter.convert(
result.getObject(binding.column()), binding.field().getType()));
}
return instance;
} catch (ReflectiveOperationException exception) {
throw new DatabaseExecutionException(0, "Unable to map bean " + type.getName(), exception);
}
};
}
}
private record FieldBinding(Field field, Integer column) {}
}

View File

@@ -0,0 +1,47 @@
package com.cygnus.db;
import java.sql.Array;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.UUID;
final class ParameterBinder {
void bind(PreparedStatement statement, Object[] parameters) throws SQLException {
Object[] values = parameters == null ? new Object[0] : parameters;
for (int index = 0; index < values.length; index++) {
bind(statement, index + 1, values[index]);
}
}
private void bind(PreparedStatement statement, int index, Object value) throws SQLException {
if (value instanceof SqlArrayParameter parameter) {
Array array = statement.getConnection().createArrayOf(
parameter.elementType(), parameter.values());
statement.setArray(index, array);
} else if (value instanceof SqlParameter parameter) {
if (parameter.value() == null) statement.setNull(index, parameter.sqlType());
else statement.setObject(index, parameter.value(), parameter.sqlType());
} else if (value == null) {
statement.setObject(index, null);
} else if (value instanceof Instant instant) {
statement.setTimestamp(index, Timestamp.from(instant));
} else if (value instanceof LocalDate date) {
statement.setObject(index, date);
} else if (value instanceof LocalDateTime dateTime) {
statement.setObject(index, dateTime);
} else if (value instanceof OffsetDateTime dateTime) {
statement.setObject(index, dateTime);
} else if (value instanceof UUID uuid) {
statement.setObject(index, uuid);
} else if (value instanceof Enum<?> enumValue) {
statement.setString(index, enumValue.name());
} else {
statement.setObject(index, value);
}
}
}

View File

@@ -0,0 +1,29 @@
package com.cygnus.db;
import java.util.Objects;
import java.util.regex.Pattern;
public record QueryDefinition(int queryId, QueryType type, String sql) {
private static final String DELIMITER = "!C0L!";
private static final Pattern LEGACY_PARAMETER = Pattern.compile("\\bval\\d+\\b");
public QueryDefinition {
if (queryId <= 0) throw new QueryDefinitionException("Query ID must be positive");
Objects.requireNonNull(type, "type");
if (sql == null || sql.isBlank()) throw new QueryDefinitionException("Query SQL is empty: " + queryId);
if (LEGACY_PARAMETER.matcher(sql).find()) {
throw new QueryDefinitionException(
"Query " + queryId + " has not been migrated to parameterized SQL");
}
}
public static QueryDefinition parse(int queryId, String storedValue) {
if (storedValue == null) throw new QueryDefinitionException("Query was not found: " + queryId);
int delimiter = storedValue.indexOf(DELIMITER);
if (delimiter <= 0) throw new QueryDefinitionException("Invalid query definition: " + queryId);
return new QueryDefinition(
queryId,
QueryType.parse(storedValue.substring(0, delimiter)),
storedValue.substring(delimiter + DELIMITER.length()).trim());
}
}

View File

@@ -0,0 +1,6 @@
package com.cygnus.db;
public final class QueryDefinitionException extends RuntimeException {
public QueryDefinitionException(String message) { super(message); }
public QueryDefinitionException(String message, Throwable cause) { super(message, cause); }
}

View File

@@ -0,0 +1,6 @@
package com.cygnus.db;
@FunctionalInterface
public interface QueryDefinitionProvider {
QueryDefinition get(int queryId);
}

View File

@@ -0,0 +1,15 @@
package com.cygnus.db;
import java.util.Locale;
public enum QueryType {
SELECT, INSERT, UPDATE, DELETE, PROCEDURE;
public static QueryType parse(String value) {
try {
return valueOf(value.trim().toUpperCase(Locale.ROOT));
} catch (RuntimeException exception) {
throw new QueryDefinitionException("Unsupported query type: " + value, exception);
}
}
}

View File

@@ -0,0 +1,6 @@
package com.cygnus.db;
@FunctionalInterface
public interface RowConsumer<T> {
void accept(T row) throws Exception;
}

View File

@@ -0,0 +1,9 @@
package com.cygnus.db;
import java.sql.ResultSet;
import java.sql.SQLException;
@FunctionalInterface
public interface RowMapper<T> {
T map(ResultSet resultSet) throws SQLException;
}

View File

@@ -0,0 +1,9 @@
package com.cygnus.db;
import java.util.Map;
public interface RowView {
Object get(String column);
<T> T get(String column, Class<T> type);
Map<String, Object> asMap();
}

View File

@@ -0,0 +1,57 @@
package com.cygnus.db;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
import java.util.regex.Pattern;
/**
* A typed SQL array parameter for PostgreSQL expressions such as
* {@code column_name = ANY(?)}.
*/
public record SqlArrayParameter(String elementType, Object[] values) {
private static final Pattern TYPE_NAME = Pattern.compile(
"[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)?");
public SqlArrayParameter {
elementType = Objects.requireNonNull(elementType, "elementType").trim();
if (!TYPE_NAME.matcher(elementType).matches()) {
throw new IllegalArgumentException("Invalid SQL array element type: " + elementType);
}
values = Objects.requireNonNull(values, "values").clone();
}
@Override
public Object[] values() {
return values.clone();
}
public static SqlArrayParameter of(String elementType, Object... values) {
return new SqlArrayParameter(elementType, values);
}
public static SqlArrayParameter of(String elementType, Collection<?> values) {
Objects.requireNonNull(values, "values");
return new SqlArrayParameter(elementType, values.toArray());
}
public static SqlArrayParameter text(String... values) { return of("text", (Object[]) values); }
public static SqlArrayParameter text(Collection<String> values) { return of("text", values); }
public static SqlArrayParameter integer(Integer... values) { return of("integer", (Object[]) values); }
public static SqlArrayParameter smallint(Short... values) { return of("smallint", (Object[]) values); }
public static SqlArrayParameter bigint(Long... values) { return of("bigint", (Object[]) values); }
public static SqlArrayParameter numeric(BigDecimal... values) { return of("numeric", (Object[]) values); }
public static SqlArrayParameter bool(Boolean... values) { return of("boolean", (Object[]) values); }
public static SqlArrayParameter uuid(UUID... values) { return of("uuid", (Object[]) values); }
public static SqlArrayParameter date(LocalDate... values) { return of("date", (Object[]) values); }
public static SqlArrayParameter timestamp(LocalDateTime... values) {
return of("timestamp", (Object[]) values);
}
public static SqlArrayParameter timestampWithTimeZone(OffsetDateTime... values) {
return of("timestamptz", (Object[]) values);
}
}

View File

@@ -0,0 +1,6 @@
package com.cygnus.db;
public record SqlParameter(int sqlType, Object value) {
public static SqlParameter of(int sqlType, Object value) { return new SqlParameter(sqlType, value); }
public static SqlParameter nullValue(int sqlType) { return new SqlParameter(sqlType, null); }
}

View File

@@ -0,0 +1,6 @@
package com.cygnus.db;
@FunctionalInterface
public interface TransactionCallback<T> {
T execute(TransactionOperations operations) throws Exception;
}

View File

@@ -0,0 +1,12 @@
package com.cygnus.db;
import java.util.List;
import java.util.Optional;
public interface TransactionOperations {
<T> List<T> query(int queryId, Object[] parameters, Class<T> responseType);
<T> Optional<T> queryOne(int queryId, Object[] parameters, Class<T> responseType);
int insert(int queryId, Object[] parameters);
int update(int queryId, Object[] parameters);
int delete(int queryId, Object[] parameters);
}

View File

@@ -0,0 +1,79 @@
package com.cygnus.db;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.UUID;
final class ValueConverter {
private ValueConverter() {}
@SuppressWarnings({"unchecked", "rawtypes"})
static <T> T convert(Object value, Class<T> target) {
if (value == null) return target.isPrimitive() ? (T) primitiveDefault(target) : null;
Class<?> boxed = box(target);
if (boxed.isInstance(value)) return (T) value;
if (boxed == String.class) return (T) value.toString();
if (Number.class.isAssignableFrom(boxed) && value instanceof Number number) {
return (T) number(number, boxed);
}
if (boxed == Boolean.class) {
if (value instanceof Number number) return (T) Boolean.valueOf(number.intValue() != 0);
return (T) Boolean.valueOf(value.toString());
}
if (boxed == Character.class) {
String text = value.toString();
if (text.isEmpty()) throw new IllegalArgumentException("Cannot convert an empty value to char");
return (T) Character.valueOf(text.charAt(0));
}
if (boxed == UUID.class) return (T) UUID.fromString(value.toString());
if (boxed == LocalDate.class && value instanceof Date date) return (T) date.toLocalDate();
if (boxed == LocalDateTime.class && value instanceof Timestamp timestamp) return (T) timestamp.toLocalDateTime();
if (boxed == Instant.class && value instanceof Timestamp timestamp) return (T) timestamp.toInstant();
if (boxed == OffsetDateTime.class && value instanceof OffsetDateTime dateTime) return (T) dateTime;
if (boxed.isEnum()) return (T) Enum.valueOf((Class<Enum>) boxed, value.toString());
throw new DatabaseExecutionException(0,
"Cannot convert " + value.getClass().getName() + " to " + target.getName(), null);
}
private static Object number(Number value, Class<?> target) {
if (target == Integer.class) return value.intValue();
if (target == Long.class) return value.longValue();
if (target == Double.class) return value.doubleValue();
if (target == Float.class) return value.floatValue();
if (target == Short.class) return value.shortValue();
if (target == Byte.class) return value.byteValue();
if (target == BigDecimal.class) return value instanceof BigDecimal decimal
? decimal : new BigDecimal(value.toString());
return value;
}
private static Class<?> box(Class<?> type) {
if (!type.isPrimitive()) return type;
if (type == int.class) return Integer.class;
if (type == long.class) return Long.class;
if (type == double.class) return Double.class;
if (type == float.class) return Float.class;
if (type == short.class) return Short.class;
if (type == byte.class) return Byte.class;
if (type == boolean.class) return Boolean.class;
if (type == char.class) return Character.class;
return type;
}
private static Object primitiveDefault(Class<?> type) {
if (type == boolean.class) return false;
if (type == char.class) return '\0';
if (type == byte.class) return (byte) 0;
if (type == short.class) return (short) 0;
if (type == int.class) return 0;
if (type == long.class) return 0L;
if (type == float.class) return 0F;
if (type == double.class) return 0D;
return null;
}
}

View File

@@ -0,0 +1,86 @@
package com.cygnus.db;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.sql.Connection;
import java.sql.Statement;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class JdbcCygnusDbExecutorTest {
private JdbcCygnusDbExecutor executor;
@BeforeEach
void setUp() throws Exception {
JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setURL("jdbc:h2:mem:cygnus;DB_CLOSE_DELAY=-1");
try (Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement()) {
statement.execute("DROP TABLE IF EXISTS person");
statement.execute("CREATE TABLE person (id bigint generated by default as identity primary key, name varchar(100), active boolean)");
statement.execute("INSERT INTO person(name, active) VALUES ('Asha', true), ('Ravi', false)");
}
Map<Integer, QueryDefinition> queries = Map.of(
1, new QueryDefinition(1, QueryType.SELECT,
"select id, name from person where active = ? order by id"),
2, new QueryDefinition(2, QueryType.INSERT,
"insert into person(name, active) values (?, ?)"),
3, new QueryDefinition(3, QueryType.UPDATE,
"update person set active = ? where name = ?"),
4, new QueryDefinition(4, QueryType.DELETE,
"delete from person where name = ?"),
5, new QueryDefinition(5, QueryType.PROCEDURE, "call abs(?)"));
executor = new JdbcCygnusDbExecutor(dataSource, queries::get, new ExecutorOptions(10, 5));
}
@Test
void mapsSelectDirectlyToRecordsAndDynamicRows() {
var people = executor.query(1, new Object[]{true}, Person.class);
assertEquals(java.util.List.of(new Person(1L, "Asha")), people);
RowView row = executor.query(1, new Object[]{true}).getFirst();
assertEquals("Asha", row.get("NAME", String.class));
assertEquals(1L, row.get("id", Long.class));
}
@Test
void executesInsertUpdateDeleteAndGeneratedKeys() {
Long id = executor.insert(2, new Object[]{"Mira", true}, Long.class);
assertTrue(id > 0);
assertEquals(1, executor.update(3, new Object[]{false, "Mira"}));
assertEquals(1, executor.delete(4, new Object[]{"Mira"}));
}
@Test
void executesProcedureAndStreamsWithoutMaterializingRows() {
assertEquals(7, executor.procedure(5, new Object[]{-7}, Integer.class));
AtomicInteger count = new AtomicInteger();
executor.stream(1, new Object[]{true},
result -> new Person(result.getLong("id"), result.getString("name")),
row -> count.incrementAndGet());
assertEquals(1, count.get());
}
@Test
void rollsBackFailedTransactions() {
assertThrows(IllegalStateException.class, () -> executor.transaction(tx -> {
tx.insert(2, new Object[]{"Rollback", true});
throw new IllegalStateException("stop");
}));
assertFalse(executor.query(1, new Object[]{true}, Person.class).stream()
.anyMatch(person -> person.name().equals("Rollback")));
}
@Test
void rejectsLegacyStringReplacementQueries() {
assertThrows(QueryDefinitionException.class, () ->
QueryDefinition.parse(513, "select!C0L!select * from person where id=val0"));
}
record Person(long id, String name) {}
}

View File

@@ -0,0 +1,98 @@
package com.cygnus.db;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import java.lang.reflect.Proxy;
import java.sql.Array;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
class ParameterBinderTest {
@Test
void bindsDynamicTextCollectionAsOneTypedSqlArray() throws Exception {
BindingCapture capture = new BindingCapture();
new ParameterBinder().bind(capture.statement(), new Object[] {
SqlArrayParameter.text(List.of("CATEGORY", "PRODUCT"))
});
assertEquals("text", capture.elementType.get());
assertArrayEquals(new Object[] {"CATEGORY", "PRODUCT"}, capture.values.get());
assertEquals(1, capture.parameterIndex.get());
}
@Test
void supportsUuidAndEmptyArrays() throws Exception {
UUID id = UUID.randomUUID();
BindingCapture uuidCapture = new BindingCapture();
new ParameterBinder().bind(uuidCapture.statement(),
new Object[] {SqlArrayParameter.uuid(id)});
assertEquals("uuid", uuidCapture.elementType.get());
assertArrayEquals(new Object[] {id}, uuidCapture.values.get());
BindingCapture emptyCapture = new BindingCapture();
new ParameterBinder().bind(emptyCapture.statement(),
new Object[] {SqlArrayParameter.integer()});
assertEquals("integer", emptyCapture.elementType.get());
assertArrayEquals(new Object[0], emptyCapture.values.get());
}
private static final class BindingCapture {
private final AtomicReference<String> elementType = new AtomicReference<>();
private final AtomicReference<Object[]> values = new AtomicReference<>();
private final AtomicInteger parameterIndex = new AtomicInteger();
private final Array sqlArray = proxy(Array.class, (method, args) -> defaultValue(method.getReturnType()));
PreparedStatement statement() {
Connection connection = proxy(Connection.class, (method, args) -> {
if (method.getName().equals("createArrayOf")) {
elementType.set((String) args[0]);
values.set(((Object[]) args[1]).clone());
return sqlArray;
}
return defaultValue(method.getReturnType());
});
return proxy(PreparedStatement.class, (method, args) -> {
if (method.getName().equals("getConnection")) return connection;
if (method.getName().equals("setArray")) {
parameterIndex.set((Integer) args[0]);
assertSame(sqlArray, args[1]);
return null;
}
return defaultValue(method.getReturnType());
});
}
}
@SuppressWarnings("unchecked")
private static <T> T proxy(Class<T> type, Handler handler) {
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[] {type},
(proxy, method, args) -> handler.invoke(method, args));
}
private static Object defaultValue(Class<?> type) {
if (!type.isPrimitive()) return null;
if (type == boolean.class) return false;
if (type == byte.class) return (byte) 0;
if (type == short.class) return (short) 0;
if (type == int.class) return 0;
if (type == long.class) return 0L;
if (type == float.class) return 0F;
if (type == double.class) return 0D;
if (type == char.class) return '\0';
return null;
}
@FunctionalInterface
private interface Handler {
Object invoke(java.lang.reflect.Method method, Object[] args) throws Throwable;
}
}

2
index.sql Normal file
View File

@@ -0,0 +1,2 @@
CREATE INDEX idx_portfolio_branch_active_name
ON portfolio (branch_id, isactive, portname);

View File

@@ -491,7 +491,7 @@ Query509!C0L!procedure!C0L!select changeuserstatus(val0,val1)
Query510!C0L!select!C0L!select u.user_id,u.loginid,u.displayname,u.group_id,u.emailid from app_user u where u.user_id=val0
Query511!C0L!select!C0L!select group_id,name from user_group where isactive = 1 and group_id in (select c.child_id from user_group_childs c where c.group_id = val0)
Query512!C0L!procedure!C0L!select updateuser(val0,'val1','val2',val3,'val4',val5,val6,val7,val8)
Query513!C0L!procedure!C0L!select startmaintenance('val0',val1,'val2')
Query513!C0L!procedure!C0L!select startmaintenance(?,?,?)
Query514!C0L!select!C0L!select m.uuid,mc.portname,mc.mvcode,mc.applno,mc.customername,mc.address,mc.visit,mc.uid from mnimble_cases mc,main m where mc.submittedon is not null and mc.case_id = m.case_id and (lower(mc.mvcode) = lower('val0') or lower(mc.applno) = lower('val0'))
Query515!C0L!procedure!C0L!select pendingmnimblecase(val0,'val1',val2)
Query516!C0L!select!C0L!select uid,emailid from emailids
@@ -515,4 +515,4 @@ Query533!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(rec
Query534!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as recdate,applno,upper(customername) cname,bank_branch_id,upper(name) as pname1,'RESIDENCE VERIFICATION REPORT' as label1,'Agency' as label2,upper(maincompany) as agencyname, 'Received Date' as label4,to_char(receivedate,'DD/MM/YYYY') as recdate,'Application No' as label5,upper(applno) as applno2,'Product' as label6,upper(product) as product1,'Landmark' as label7,upper(landmark) as loanamount,'Visit Date' as label8,visitdate,'Visit Time' as label9,visittime,'Report Date' as label10,to_char(sdoneon,'DD/MM/YYYY') as sdoneon,'Applicant''s Name' as label11,upper(customername) as custname1,'Address' as label13,upper(raddr) as raddr,'Met Person' as label14,upper(metperson) as metperson,'Relation' as label15,(case when upper(metrela)='OTHERS' then upper(metrealoth) else upper(metrela) end) as metrela,'Ownership' as label16,(case when upper(rowner)='OTHERS' then upper(owneroth) else upper(rowner) end) as rowner,'Stay at Curr Address' as label17,coalesce(((case when (yrsadd<>'0' and yrsadd<>'') then yrsadd||' YEARS(S) ' else '' end)||(case when (mnthadd <> '0' and mnthadd <> '') then mnthadd||' MONTH(S)' else '' end)),'NA') as rstay,'Marital Status' as label18,upper(maritial) as marital,'Total Family Members' as label19,totmem,'Total Earning Members' as label20,earning,'Spouse Working' as label21,upper(spswork) as spswork,'Spouse Employment Details' as label22,upper(spsempdet) as spsempdet,'Residence Proof' as label23,upper(proofdet) as resproof,'Ease of Location' as label24,upper(raccess) as raccess,'Locality' as label25,(case when upper(locality)='OTHERS' then upper(locaoth) else upper(locality) end) as locality,'Residence Type' as lblrtype,(case when upper(restype)='OTHERS' then upper(typeoth) else upper(restype) end) as restype,'Name Plate' as label26,(case when upper(nameplate)='YES' then upper(platedet) else upper(nameplate) end) nameplate,'Neighbour Reference' as label27,(case when upper(field30_9)='-1' then 'NA' else upper(field30_9) end) as neref,'Neighbour Feedback' as label28,upper(fieldtext_1) as nerem,'Any Political Links' as label29,upper(political) as political,'If Yes, Details' as label30,upper(politicdet) as politicdet,'Verifier Remarks' as label31,upper(verremarks) as verrem,'Final Status' as label33,upper(status) as status,'Remarks' as label34,upper(remarks) as remarks,'Scanned By' as label35,upper(reportdoneby),'Verifier Name' as label35,upper(rvername),'Agency Seal','images/signs/'||sdoneby||'.png' as singature,(case when portfolio_id=19 OR portfolio_id=43 then 'Photographs' else '{SK!PTH15D@TA}' end) as lblphotographs,coalesce((case when portfolio_id=19 OR portfolio_id=43 then sublist('select docpath as field from tagged_docs where isdeleted=0 and doctype=''REP_ATTACH'' and vistype=''R'' and uuid='''||r.uuid||'''',',') else '{SK!PTH15D@TA}' end),'NA') as photographs from banklist_main_residence r where (val0) val1
Query535!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as recdate,applno,upper(customername) cname,bank_branch_id,upper(name) as pname1,'OFFICE VERIFICATION REPORT' as label1,'Agency' as label2,upper(maincompany) as agenname,'Received Date' as label4,to_char(receivedate,'DD/MM/YYYY') as receivedate,'Application No' as label5,upper(applno) as applno1,'Product' as label6,upper(product) as product1,'Landmark' as label7,'' as landmark,'Visit Date' as label8,visitdate,'Visit time' as label9,visittime,'Report Date' as label10,to_char(sdoneon,'DD/MM/YYYY') as sdoneon,'Applicants Name' as label11,upper(customername) as customername2,'Occupation' as label12,upper(category) as category1,'Address' as label13,upper(oaddr) as oaddr,'Met Person' as label14,upper(metperson) as metperson,'Relation' as label15,(case when upper(metrela)='OTHERS' then upper(metrealoth) else upper(metrela) end) as metrela,'Ownership' as label16,(case when upper(ownership)='OTHERS' then upper(owneroth) else upper(ownership) end) as ownership,'Job/Biz at Curr Add' as label17,((case when (yrsjob<>'0' and yrsjob<>'') then yrsjob||' YEARS(S) ' else '' end)||(case when (mnthjob<> '0' and mnthjob <> '') then mnthjob||' MONTH(S)' else '' end)) as yrsatjob,'Designation'as label18,(case when upper(appdesign)='OTHERS' then upper(appdesignoth) else upper(appdesign) end) as appdesign,'Department' as label19,upper(appdept) as appdept,'Reporting To' as label20,upper(reportingto) as reportingto,'Salary' as label21,upper(saldetail) as saldetail,'NOB' as label22,(case when upper(compprof)='OTHERS' then upper(compprofoth) else upper(compprof) end) as compprof,'Board Seen' as lblboardseen, (case when bizboard='-1' then 'NA' else upper(bizboard) end) as bizboard,'Ease of Access' as label27,upper(easeloc) as easeloc,'Locality' as label28,(case when upper(locality)='OTHERS' then upper(locaoth) else upper(locality) end) as locality,'Organisation Type' as label29,upper(busitype) as orgtype,'Neighbour Reference' as label30,(case when upper(field30_9)='-1' then 'NA' else upper(field30_9) end) as neref,'Neighbour Feedback' as label31,upper(fieldtext_1) as negrem,'Final Status' as label36,upper(status) as status,'Remarks' as label37,upper(remarks) as remarks,'Scanned By' as label38,upper(reportdoneby) as doneby,'Verifier Name' as lblvername, upper(overname) as overname, 'Agency Seal'as lbl,'images/signs/'||sdoneby||'.png' as singature,(case when portfolio_id=19 OR portfolio_id=43 then 'Photographs' else '{SK!PTH15D@TA}' end) as lblphotographs,coalesce((case when portfolio_id=19 OR portfolio_id=43 then sublist('select docpath as field from tagged_docs where isdeleted=0 and doctype=''REP_ATTACH'' and vistype=''O'' and uuid='''||o.uuid||'''',',') else '{SK!PTH15D@TA}' end),'NA') as photographs from banklist_main_office o where (val0) val1
Query536!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as recdate,applno,upper(customername) as cname,bank_branch_id,'MATRIX - Residence Report','Client Name:- '||upper(name),'Product:- '||upper(product),'Application ID:- '||upper(applno) as applno1,'Applicant Name:- '||upper(customername) as cname1,'Verification Type:-','Residence:','images/tick.gif' as chkresidence,'RCO:',(case when upper(Field15_1)='YES' then 'images/tick.gif' else 'images/untick.jpg' end) as chkrco,'Office:','images/untick.jpg' as chkoffice,'Date & Time of Visit: - '||coalesce(visitdate||' ','')||coalesce(visittime,''),'Address of Visit:','Residence Address: -',upper(raddr) as raddr,'Office Address: -','','Remarks & Observations:',upper(remarks) as remarks,'Status:',upper(status),'Check List:','Residence:','1.','Stay Confirm:','Yes',(case when upper(Field30_3)='YES' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_3)='NO' then 'images/tick.gif' else 'images/untick.jpg' end),'2.','Location Type:','Slum',(case when upper(locality)='SLUM' then 'images/tick.gif' else 'images/untick.jpg' end),'Chawl',(case when upper(locality)='CHAWL TYPE' then 'images/tick.gif' else 'images/untick.jpg' end),'UMC',(case when upper(locality)='UPPER MIDDLE CLASS' then 'images/tick.gif' else 'images/untick.jpg' end),'LMC',(case when upper(locality)='LOWER MIDDLE CLASS' then 'images/tick.gif' else 'images/untick.jpg' end),'','Com-Dom',(case when upper(locality)='COM-DOM' then 'images/tick.gif' else 'images/untick.jpg' end),'Poor',(case when upper(locality)='POOR' then 'images/tick.gif' else 'images/untick.jpg' end),'3.','TPC Confirm:','Yes',(case when upper(Field30_9)='POSITIVE' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_9)='NEGATIVE' then 'images/tick.gif' else 'images/untick.jpg' end),'4.','Name Board:',upper(platedet),'','Business:','1.','Working Confirm:','Yes','images/untick.jpg','No','images/untick.jpg','2.','Name board Match with the application details','Yes','images/untick.jpg','No','images/untick.jpg','3.','Nature of Business:','','','Field Executive Name: - '||upper(rvername) as vername,'Reports Prepared By: - '||upper(sdonebyname),'Manager/Authority Name & Sign:','images/signs/'||supervisor||'.png' as singature,(case portfolio_id when 194 then 'Images:' else '{SK!PTH15D@TA}' end) as lblphoto,(case portfolio_id when 194 then sublist('select docpath as field from tagged_docs where isdeleted=0 and doctype=''REP_ATTACH'' and vistype=''R'' and uuid='''||r.uuid||'''',',') else '{SK!PTH15D@TA}' end) as photographs from banklist_main_residence r where (val0) val1
Query537!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as recdate,applno,upper(customername) as cname,bank_branch_id,'MATRIX - Office Report','Client Name:- '||upper(name),'Product:- '||upper(product),'Application ID:- '||upper(applno) as applno1,'Applicant Name:- '||upper(customername) as cname1,'Verification Type:-','Residence:','images/untick.jpg' as chkresidence,'RCO:',(case when upper(Field15_1)='YES' then 'images/tick.gif' else 'images/untick.jpg' end) as chkrco,'Office:','images/tick.gif' as chkoffice,'Date & Time of Visit: - '||coalesce(visitdate||' ','')||coalesce(visittime,''),'Address of Visit:','Residence Address: -','' as raddr,'Office Address: -',upper(oaddr) as oaddr,'Remarks & Observations:',upper(remarks) as remarks,'Status:',upper(status),'Check List:','Residence:','1.','Stay Confirm:','Yes','images/untick.jpg','No','images/untick.jpg','2.','Location Type:','Slum','images/untick.jpg','Chawl','images/untick.jpg','UMC','images/untick.jpg','LMC','images/untick.jpg','','Com-Dom','images/untick.jpg','Poor','images/untick.jpg','3.','TPC Confirm:','Yes',(case when upper(Field30_9)='POSITIVE' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_9) <> 'POSITIVE' AND upper(Field30_9) <> 'NA' then 'images/tick.gif' else 'images/untick.jpg' end),'4.','Name Board:','','','Business:','1.','Working Confirm:','Yes',(case when upper(Field30_22)='YES' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_22) <> 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'2.','Name board Match with the application details','Yes',(case when upper(Field30_2) = 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_2) <> 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'3.','Nature of Business:',upper(proddealt),'','Field Executive Name: - '||upper(overname) as vername,'Reports Prepared By: - '||upper(sdonebyname),'Manager/Authority Name & Sign:','images/signs/'||supervisor||'.png' as singature,(case portfolio_id when 194 then 'Images:' else '{SK!PTH15D@TA}' end) as lblphoto,(case portfolio_id when 194 then sublist('select docpath as field from tagged_docs where isdeleted=0 and doctype=''REP_ATTACH'' and vistype=''O'' and uuid='''||o.uuid||'''',',') else '{SK!PTH15D@TA}' end) as photographs from banklist_main_office o where (val0) val1
Query537!C0L!select!C0L!select uuid,product,maincompany,bkbranchname,to_char(receivedate,'DD/MM/YYYY') as recdate,applno,upper(customername) as cname,bank_branch_id,'MATRIX - Office Report','Client Name:- '||upper(name),'Product:- '||upper(product),'Application ID:- '||upper(applno) as applno1,'Applicant Name:- '||upper(customername) as cname1,'Verification Type:-','Residence:','images/untick.jpg' as chkresidence,'RCO:',(case when upper(Field15_1)='YES' then 'images/tick.gif' else 'images/untick.jpg' end) as chkrco,'Office:','images/tick.gif' as chkoffice,'Date & Time of Visit: - '||coalesce(visitdate||' ','')||coalesce(visittime,''),'Address of Visit:','Residence Address: -','' as raddr,'Office Address: -',upper(oaddr) as oaddr,'Remarks & Observations:',upper(remarks) as remarks,'Status:',upper(status),'Check List:','Residence:','1.','Stay Confirm:','Yes','images/untick.jpg','No','images/untick.jpg','2.','Location Type:','Slum','images/untick.jpg','Chawl','images/untick.jpg','UMC','images/untick.jpg','LMC','images/untick.jpg','','Com-Dom','images/untick.jpg','Poor','images/untick.jpg','3.','TPC Confirm:','Yes',(case when upper(Field30_9)='POSITIVE' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_9) <> 'POSITIVE' AND upper(Field30_9) <> 'NA' then 'images/tick.gif' else 'images/untick.jpg' end),'4.','Name Board:','','','Business:','1.','Working Confirm:','Yes',(case when upper(Field30_22)='YES' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_22) <> 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'2.','Name board Match with the application details','Yes',(case when upper(Field30_2) = 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'No',(case when upper(Field30_2) <> 'YES' then 'images/tick.gif' else 'images/untick.jpg' end),'3.','Nature of Business:',upper(proddealt),'','Field Executive Name: - '||upper(overname) as vername,'Reports Prepared By: - '||upper(sdonebyname),'Manager/Authority Name & Sign:','images/signs/'||supervisor||'.png' as singature,(case portfolio_id when 194 then 'Images:' else '{SK!PTH15D@TA}' end) as lblphoto,(case portfolio_id when 194 then sublist('select docpath as field from tagged_docs where isdeleted=0 and doctype=''REP_ATTACH'' and vistype=''O'' and uuid='''||o.uuid||'''',',') else '{SK!PTH15D@TA}' end) as photographs from banklist_main_office o where (val0) val1

View File

@@ -12,6 +12,8 @@
<name>Cygnus Platform</name>
<modules>
<module>cygnus-lib</module>
<module>cygnus-onprem-db</module>
<module>cygnus-cloud-service</module>
<module>cygnus-cloud-client</module>
<module>cygnus-installer</module>
@@ -37,5 +39,6 @@
<vertx.version>5.1.5</vertx.version>
<reactor.netty.version>1.2.8</reactor.netty.version>
<nimbus.version>10.4</nimbus.version>
<lombok.version>1.18.46</lombok.version>
</properties>
</project>