diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
index bd4b997..6a18fbd 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
@@ -151,7 +151,7 @@ $(document).ready(function(){
document.form1.submit();
});
- //결재상신 (Amaranth10 연동)
+ //결재상신
$("#btnApproval").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length<1){
@@ -239,7 +239,7 @@ $(document).ready(function(){
}
});
} else {
- // 신규수주 또는 가격인하 → Amaranth10 결재 상신 팝업
+ // 신규수주 또는 가격인하 → 결재필요
var reasonText = "";
if(reason == "신규수주") {
reasonText = "신규수주입니다.";
@@ -249,7 +249,7 @@ $(document).ready(function(){
Swal.fire({
title: '결재상신',
- html: (reasonText ? reasonText + '
' : '') + '결재상신 하시겠습니까?
* Amaranth10 전자결재로 상신됩니다.',
+ html: (reasonText ? reasonText + '
' : '') + '결재상신 하시겠습니까?
* 결재완료 후 메일발송이 가능합니다.',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
@@ -258,19 +258,21 @@ $(document).ready(function(){
cancelButtonText: '취소'
}).then((result) => {
if(result.isConfirmed) {
- var docTitle = encodeURIComponent(fnc_checkNull(selectedData[0].CONTRACT_NO));
- fn_openAmaranthApprovalSubmit(estObjId, docTitle);
+ var objId = estObjId;
+ var title = encodeURIComponent(fnc_checkNull(selectedData[0].CONTRACT_NO));
+ var approvalUrl = "/approval/registApproval.do?targetType=CONTRACT_ESTIMATE&targetObjId="+objId+"&approvalTitle="+title;
+ window.open(approvalUrl, "registApproval", "width=700,height=700");
}
});
}
} else {
- // API 오류 시 Amaranth 결재 팝업으로 진행
- fn_openAmaranthApprovalSubmit(estObjId, encodeURIComponent(fnc_checkNull(selectedData[0].CONTRACT_NO)));
+ // API 오류 시 기존 방식으로 진행
+ fn_showApprovalConfirmSimple(estObjId, selectedData[0].CONTRACT_NO);
}
},
error: function() {
- // AJAX 오류 시 Amaranth 결재 팝업으로 진행
- fn_openAmaranthApprovalSubmit(estObjId, encodeURIComponent(fnc_checkNull(selectedData[0].CONTRACT_NO)));
+ // AJAX 오류 시 기존 방식으로 진행
+ fn_showApprovalConfirmSimple(estObjId, selectedData[0].CONTRACT_NO);
}
});
}
@@ -823,22 +825,7 @@ function fn_showSerialNoPopup(serialNoString){
});
}
-/**
- * Amaranth10 결재 상신 팝업 열기
- * @param estObjId 견적서 ObjId
- * @param docTitle 문서 제목 (URL 인코딩 상태)
- */
-function fn_openAmaranthApprovalSubmit(estObjId, docTitle) {
- var approvalUrl = "/approval/amaranthApprovalSubmit.do"
- + "?targetType=CONTRACT_ESTIMATE"
- + "&targetObjId=" + estObjId
- + "&approvalTitle=" + docTitle;
- window.open(approvalUrl, "amaranthApprovalSubmit", "width=750,height=700,menubar=no,scrollbars=yes,resizable=yes");
-}
-
-/*
-// [주석 처리] 기존 내부 결재상신 확인 다이얼로그 (단순 버전)
-// Amaranth10 연동으로 대체됨. 필요시 주석 해제하여 사용 가능
+// 결재상신 확인 다이얼로그 (단순 버전)
function fn_showApprovalConfirmSimple(estObjId, contractNo) {
Swal.fire({
title: '결재상신',
@@ -857,7 +844,6 @@ function fn_showApprovalConfirmSimple(estObjId, contractNo) {
}
});
}
-*/
// 메일 작성 팝업 열기
function fn_openMailFormPopup(contractObjId){
diff --git a/WebContent/init.jsp b/WebContent/init.jsp
index b31fdca..d0b89f0 100644
--- a/WebContent/init.jsp
+++ b/WebContent/init.jsp
@@ -39,6 +39,7 @@ pageContext.setAttribute("newLineChar", "\n");
+
diff --git a/WebContent/init_new.jsp b/WebContent/init_new.jsp
index c303e2c..50df74a 100644
--- a/WebContent/init_new.jsp
+++ b/WebContent/init_new.jsp
@@ -39,6 +39,7 @@ pageContext.setAttribute("newLineChar", "\n");
+
diff --git a/src/com/pms/api/AmaranthApprovalApiClient.java b/src/com/pms/api/AmaranthApprovalApiClient.java
index eb090c4..ad4cbf1 100644
--- a/src/com/pms/api/AmaranthApprovalApiClient.java
+++ b/src/com/pms/api/AmaranthApprovalApiClient.java
@@ -813,6 +813,196 @@ public class AmaranthApprovalApiClient {
}
}
+ /**
+ * Amaranth10 전자결재 SSO URL 생성 (결재작성 팝업용)
+ * 1단계: getAuthToken()으로 사용자 인증 토큰 발급
+ * 2단계: api99u01A02 호출로 SSO URL(fullUrl) 획득
+ *
+ * @param baseUrl Amaranth10 서버 URL
+ * @param empSeq 사용자 시퀀스
+ * @param outProcessCode 외부시스템 결재연동코드 (결재연동설정에서 등록한 코드)
+ * @param formId 그룹웨어 양식코드 (outProcessCode 대신 사용 가능)
+ * @param approKey 외부시스템 연동키 ("UB_" + UUID)
+ * @param subjectStr 결재문서 제목 (선택)
+ * @param mod 작성/보기/삭제 구분 (W:작성, V:보기, D:삭제)
+ * @param compSeq 회사 시퀀스 (선택, 겸직별 결재 시 필요)
+ * @param deptSeq 부서 시퀀스 (선택, 겸직별 결재 시 필요)
+ * @return API 응답 JSON (resultData.fullUrl 포함)
+ */
+ public String getSsoUrl(String baseUrl, String empSeq, String outProcessCode,
+ String formId, String approKey, String subjectStr,
+ String mod, String compSeq, String deptSeq) throws Exception {
+
+ System.out.println("=== Amaranth SSO URL 생성 시작 ===");
+ System.out.println("empSeq: " + empSeq + ", outProcessCode: " + outProcessCode + ", formId: " + formId);
+
+ // 1단계: 인증 토큰 발급
+ Map authResult = getAuthToken(baseUrl, empSeq);
+ if (!"true".equals(authResult.get("success"))) {
+ String errMsg = authResult.get("resultMsg");
+ if (errMsg == null || errMsg.isEmpty()) errMsg = authResult.get("error");
+ throw new Exception("인증 토큰 발급 실패: " + errMsg);
+ }
+
+ String authToken = authResult.get("authToken");
+ String userHashKey = authResult.get("hashKey");
+ System.out.println("[1단계] 인증 토큰 발급 성공");
+
+ // 2단계: SSO API 호출 (api99u01A02)
+ System.setProperty("https.protocols", "TLSv1.2");
+ setupSslTrustAll();
+
+ String urlPath = "/apiproxy/authUser/api99u01A02";
+ String cleanBaseUrl = baseUrl.endsWith("/") ? baseUrl.substring(0, baseUrl.length() - 1) : baseUrl;
+ String fullUrl = cleanBaseUrl + urlPath;
+
+ URL url = new URL(fullUrl);
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setConnectTimeout(30000);
+ connection.setReadTimeout(30000);
+ connection.setInstanceFollowRedirects(false);
+
+ try {
+ connection.setRequestMethod("POST");
+ connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+ connection.setRequestProperty("Accept", "application/json");
+ connection.setRequestProperty("callerName", CALLER_NAME);
+ connection.setRequestProperty("Authorization", "Bearer " + authToken);
+
+ String transactionId = generateTransactionId();
+ connection.setRequestProperty("transaction-id", transactionId);
+
+ String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+ connection.setRequestProperty("timestamp", timestamp);
+ connection.setRequestProperty("groupSeq", GROUP_SEQ);
+
+ // 사용자 인증 wehago-sign (발급받은 authToken + hashKey 사용)
+ String wehagoSign = generateWehagoSign(authToken, transactionId, timestamp, urlPath, userHashKey);
+ connection.setRequestProperty("wehago-sign", wehagoSign);
+
+ // empSeqEnc 재생성 (SSO body용)
+ String empSeqEnc = encryptValue(empSeq);
+
+ // 요청 본문 구성
+ String requestBody = buildSsoRequestBody(empSeqEnc, outProcessCode, formId,
+ approKey, subjectStr, mod, compSeq, deptSeq);
+
+ System.out.println("[2단계] SSO API 호출 - URL: " + fullUrl);
+ System.out.println("[2단계] Request Body: " + requestBody);
+
+ connection.setDoOutput(true);
+ connection.setDoInput(true);
+
+ OutputStreamWriter writer = new OutputStreamWriter(
+ connection.getOutputStream(), StandardCharsets.UTF_8);
+ writer.write(requestBody);
+ writer.flush();
+ writer.close();
+
+ int responseCode = connection.getResponseCode();
+ BufferedReader reader = null;
+ StringBuilder response = new StringBuilder();
+
+ try {
+ if (responseCode >= 200 && responseCode < 300) {
+ reader = new BufferedReader(new InputStreamReader(
+ connection.getInputStream(), StandardCharsets.UTF_8));
+ } else {
+ java.io.InputStream errorStream = connection.getErrorStream();
+ if (errorStream != null) {
+ reader = new BufferedReader(new InputStreamReader(
+ errorStream, StandardCharsets.UTF_8));
+ } else {
+ throw new Exception("SSO API 호출 실패: HTTP " + responseCode);
+ }
+ }
+ String line;
+ while ((line = reader.readLine()) != null) {
+ response.append(line);
+ }
+ } finally {
+ if (reader != null) reader.close();
+ }
+
+ System.out.println("[2단계] Response Code: " + responseCode);
+ System.out.println("[2단계] Response: " + response.toString());
+
+ return response.toString();
+
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ /**
+ * SSO API 요청 Body 생성 (api99u01A02)
+ */
+ private String buildSsoRequestBody(String empSeqEnc, String outProcessCode,
+ String formId, String approKey,
+ String subjectStr, String mod,
+ String compSeq, String deptSeq) {
+ StringBuilder json = new StringBuilder();
+ json.append("{");
+ json.append("\"header\":{},");
+ json.append("\"body\":{");
+ json.append("\"groupSeq\":\"").append(escapeJson(GROUP_SEQ)).append("\",");
+ json.append("\"empSeqEnc\":\"").append(escapeJson(empSeqEnc)).append("\",");
+ json.append("\"type\":\"popup\",");
+ json.append("\"popupCode\":\"UBAP036\",");
+
+ // 회사/부서 정보 (겸직별 결재 시 필요)
+ if (compSeq != null && !compSeq.isEmpty()) {
+ json.append("\"companyInfo\":{");
+ json.append("\"compSeq\":\"").append(escapeJson(compSeq)).append("\"");
+ if (deptSeq != null && !deptSeq.isEmpty()) {
+ json.append(",\"deptSeq\":\"").append(escapeJson(deptSeq)).append("\"");
+ }
+ json.append("},");
+ }
+
+ // appParams (전자결재 관련 파라미터)
+ json.append("\"appParams\":{");
+ json.append("\"approKey\":\"").append(escapeJson(approKey)).append("\",");
+ json.append("\"mod\":\"").append(escapeJson(mod)).append("\"");
+
+ // outProcessCode 또는 formId (둘 중 하나 필수)
+ if (outProcessCode != null && !outProcessCode.isEmpty()) {
+ json.append(",\"outProcessCode\":\"").append(escapeJson(outProcessCode)).append("\"");
+ }
+ if (formId != null && !formId.isEmpty()) {
+ json.append(",\"formId\":\"").append(escapeJson(formId)).append("\"");
+ }
+
+ // 제목 (선택)
+ if (subjectStr != null && !subjectStr.isEmpty()) {
+ json.append(",\"subjectStr\":\"").append(escapeJson(subjectStr)).append("\"");
+ }
+
+ json.append("}"); // appParams 닫기
+ json.append("}"); // body 닫기
+ json.append("}"); // root 닫기
+ return json.toString();
+ }
+
+ /**
+ * SSL 인증서 검증 우회 설정 (개발 환경용)
+ */
+ private void setupSslTrustAll() throws Exception {
+ TrustManager[] trustAllCerts = new TrustManager[] {
+ new X509TrustManager() {
+ public X509Certificate[] getAcceptedIssuers() { return null; }
+ public void checkClientTrusted(X509Certificate[] certs, String authType) {}
+ public void checkServerTrusted(X509Certificate[] certs, String authType) {}
+ }
+ };
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+ HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
+ HttpsURLConnection.setDefaultHostnameVerifier(new javax.net.ssl.HostnameVerifier() {
+ public boolean verify(String hostname, javax.net.ssl.SSLSession session) { return true; }
+ });
+ }
+
/**
* AES128 CBC PKCS5Padding 암호화 (loginId 또는 empSeq)
* 문서: 현재날짜(YYYYMMDDHHmmss)▦값 → AES 암호화 → Base64
diff --git a/src/com/pms/controller/ApprovalController.java b/src/com/pms/controller/ApprovalController.java
index 7438235..0780429 100644
--- a/src/com/pms/controller/ApprovalController.java
+++ b/src/com/pms/controller/ApprovalController.java
@@ -370,4 +370,47 @@ public class ApprovalController {
return "/ajax/ajaxResult";
}
+ /**
+ * Amaranth10 전자결재 SSO URL 생성 (B방식 - 팝업 직접 오픈)
+ * 업무 리스트에서 결재상신 버튼 클릭 시 호출
+ * @param request
+ * @param paramMap targetType, targetObjId, approvalTitle, outProcessCode, formId
+ * @return fullUrl이 포함된 JSON
+ */
+ @RequestMapping("/approval/getAmaranthSsoUrl.do")
+ public String getAmaranthSsoUrl(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{
+ String jsonResult = approvalService.getAmaranthSsoUrl(request, paramMap);
+ request.setAttribute("RESULT", jsonResult);
+ return "/ajax/ajaxResult";
+ }
+
+ /**
+ * Amaranth10 전자결재 콜백 API
+ * 결재 이벤트(상신/진행/종결/반려/삭제 등) 발생 시 Amaranth10에서 호출
+ * 결재연동설정의 상신~삭제 URL에 이 엔드포인트를 등록
+ * @param request
+ * @param paramMap processId, approkey, docId, docSts, userId, formId, docTitle 등
+ * @return SUCCESS 응답 JSON
+ */
+ @RequestMapping("/approval/amaranthApprovalCallback.do")
+ public String amaranthApprovalCallback(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{
+ String jsonResult = approvalService.handleAmaranthApprovalCallback(paramMap);
+ request.setAttribute("RESULT", jsonResult);
+ return "/ajax/ajaxResult";
+ }
+
+ /**
+ * Amaranth10 전자결재 본문 조회 API (결재작성 시 호출)
+ * 결재연동설정의 결재작성 URL에 이 엔드포인트를 등록
+ * @param request
+ * @param paramMap approkey, formId, docId, userId, empSeq 등
+ * @return 제목/본문 JSON
+ */
+ @RequestMapping("/approval/amaranthApprovalContents.do")
+ public String amaranthApprovalContents(HttpServletRequest request, @RequestParam Map paramMap)throws Exception{
+ String jsonResult = approvalService.getAmaranthApprovalContents(paramMap);
+ request.setAttribute("RESULT", jsonResult);
+ return "/ajax/ajaxResult";
+ }
+
}
diff --git a/src/com/pms/service/ApprovalService.java b/src/com/pms/service/ApprovalService.java
index 1084e77..62b88a5 100644
--- a/src/com/pms/service/ApprovalService.java
+++ b/src/com/pms/service/ApprovalService.java
@@ -1767,6 +1767,152 @@ public class ApprovalService {
// Amaranth10 그룹 시퀀스 (상신 Body 구성용)
private static final String GROUP_SEQ = "gcmsAmaranth40578";
+ private static final String AMARANTH_BASE_URL = "https://erp.rps-korea.com";
+
+ /**
+ * Amaranth10 전자결재 SSO URL 생성 (B방식 - 팝업 직접 오픈)
+ * 1단계: 인증 토큰 발급 (api99u01A01)
+ * 2단계: SSO 호출 (api99u01A02) → fullUrl 획득
+ *
+ * @param request HttpServletRequest (세션에서 사용자 정보 추출)
+ * @param paramMap targetType, targetObjId, approvalTitle, outProcessCode, formId 등
+ * @return fullUrl이 포함된 JSON 문자열
+ */
+ public String getAmaranthSsoUrl(HttpServletRequest request, Map paramMap){
+ try {
+ HttpSession session = request.getSession();
+ PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
+ if(person == null){
+ return "{\"resultCode\":-1,\"resultMsg\":\"세션이 만료되었습니다. 다시 로그인하세요.\"}";
+ }
+
+ String empSeq = CommonUtils.checkNull(person.getEmpseq());
+ if(empSeq.isEmpty()){
+ return "{\"resultCode\":-1,\"resultMsg\":\"empSeq 정보가 없습니다. 관리자에게 문의하세요.\"}";
+ }
+
+ // 파라미터 추출
+ String targetType = CommonUtils.checkNull(paramMap.get("targetType"));
+ String targetObjId = CommonUtils.checkNull(paramMap.get("targetObjId"));
+ String approvalTitle = CommonUtils.checkNull(paramMap.get("approvalTitle"));
+ String outProcessCode = CommonUtils.checkNull(paramMap.get("outProcessCode"));
+ String formId = CommonUtils.checkNull(paramMap.get("formId"));
+ String compSeq = CommonUtils.checkNull(paramMap.get("compSeq"), "1000");
+ String deptSeq = CommonUtils.checkNull(paramMap.get("deptSeq"));
+
+ // approKey 생성: "UB_" + UUID (외부시스템과 결재문서 매핑 키)
+ String approKey = "UB_" + java.util.UUID.randomUUID().toString();
+
+ System.out.println("=== Amaranth SSO URL 생성 ===");
+ System.out.println("empSeq: " + empSeq);
+ System.out.println("targetType: " + targetType + ", targetObjId: " + targetObjId);
+ System.out.println("outProcessCode: " + outProcessCode + ", formId: " + formId);
+ System.out.println("approKey: " + approKey);
+
+ // API 호출
+ com.pms.api.AmaranthApprovalApiClient apiClient = new com.pms.api.AmaranthApprovalApiClient();
+ String apiResponse = apiClient.getSsoUrl(
+ AMARANTH_BASE_URL, empSeq, outProcessCode, formId,
+ approKey, approvalTitle, "W", compSeq, deptSeq
+ );
+
+ System.out.println("SSO API 응답: " + apiResponse);
+
+ // 응답에서 fullUrl 추출하여 approKey와 함께 반환
+ String fullUrl = extractJsonStringValue(apiResponse, "fullUrl");
+ String resultCode = extractJsonStringValue(apiResponse, "resultCode");
+
+ if("0".equals(resultCode) && !fullUrl.isEmpty()){
+ StringBuilder result = new StringBuilder();
+ result.append("{\"resultCode\":0,\"resultMsg\":\"SUCCESS\",\"resultData\":{");
+ result.append("\"fullUrl\":\"").append(escapeJsonValue(fullUrl)).append("\",");
+ result.append("\"approKey\":\"").append(escapeJsonValue(approKey)).append("\",");
+ result.append("\"targetType\":\"").append(escapeJsonValue(targetType)).append("\",");
+ result.append("\"targetObjId\":\"").append(escapeJsonValue(targetObjId)).append("\"");
+ result.append("}}");
+ return result.toString();
+ } else {
+ String resultMsg = extractJsonStringValue(apiResponse, "resultMsg");
+ return "{\"resultCode\":-1,\"resultMsg\":\"SSO URL 생성 실패: " + escapeJsonValue(resultMsg) + "\"}";
+ }
+
+ } catch(Exception e){
+ System.err.println("Amaranth SSO URL 생성 오류: " + e.getMessage());
+ e.printStackTrace();
+ return "{\"resultCode\":-1,\"resultMsg\":\"" + escapeJsonValue(e.getMessage()) + "\"}";
+ }
+ }
+
+ /**
+ * Amaranth10 전자결재 콜백 처리
+ * 결재 이벤트(상신/진행/종결/반려/삭제 등) 발생 시 Amaranth10에서 호출
+ *
+ * @param paramMap 콜백 파라미터 (processId, approkey, docId, docSts, userId, formId 등)
+ * @return 처리 결과 JSON
+ */
+ public String handleAmaranthApprovalCallback(Map paramMap){
+ String approkey = CommonUtils.checkNull(paramMap.get("approkey"));
+ String docId = CommonUtils.checkNull(paramMap.get("docId"));
+ String docSts = CommonUtils.checkNull(paramMap.get("docSts"));
+ String docTitle = CommonUtils.checkNull(paramMap.get("docTitle"));
+ String userId = CommonUtils.checkNull(paramMap.get("userId"));
+ String processId = CommonUtils.checkNull(paramMap.get("processId"));
+
+ System.out.println("=== Amaranth 결재 콜백 수신 ===");
+ System.out.println("approkey: " + approkey);
+ System.out.println("docId: " + docId);
+ System.out.println("docSts: " + docSts + " (" + getDocStsName(docSts) + ")");
+ System.out.println("docTitle: " + docTitle);
+ System.out.println("userId: " + userId);
+ System.out.println("processId: " + processId);
+ System.out.println("전체 파라미터: " + paramMap);
+
+ // TODO: approkey로 우리 시스템의 대상 문서를 찾아서 상태 업데이트
+ // 예: approval 테이블에서 approkey로 조회 → targetObjId의 상태 변경
+
+ return "{\"resultCode\":\"SUCCESS\",\"resultMessage\":\"성공하였습니다.\"}";
+ }
+
+ /**
+ * Amaranth10 결재작성 시 본문 내용 조회 (Binding WebAPI / WebAPI)
+ * 결재 작성 화면이 열릴 때 Amaranth10에서 호출
+ *
+ * @param paramMap approkey, formId, docId, userId, empSeq 등
+ * @return 제목/본문 JSON
+ */
+ public String getAmaranthApprovalContents(Map paramMap){
+ String approkey = CommonUtils.checkNull(paramMap.get("approkey"));
+ String docId = CommonUtils.checkNull(paramMap.get("docId"));
+
+ System.out.println("=== Amaranth 결재 본문 조회 ===");
+ System.out.println("approkey: " + approkey);
+ System.out.println("docId: " + docId);
+ System.out.println("전체 파라미터: " + paramMap);
+
+ // TODO: approkey로 대상 문서 데이터를 조회하여 본문 HTML 또는 Binding JSON 구성
+ // Binding WebAPI 방식일 경우 ITEMS/TABLE 구조의 JSON 반환
+
+ StringBuilder result = new StringBuilder();
+ result.append("{\"resultCode\":0,\"resultMessage\":\"SUCCESS\",\"resultData\":{");
+ result.append("\"title\":\"결재 문서\",");
+ result.append("\"contents\":\"본문 내용
\"");
+ result.append("}}");
+ return result.toString();
+ }
+
+ /**
+ * 결재 상태코드 → 한글명 변환
+ */
+ private String getDocStsName(String docSts){
+ if("10".equals(docSts)) return "임시보관";
+ if("20".equals(docSts)) return "상신";
+ if("30".equals(docSts)) return "진행";
+ if("40".equals(docSts)) return "발신종결";
+ if("90".equals(docSts)) return "종결";
+ if("100".equals(docSts)) return "반려";
+ if("110".equals(docSts)) return "보류";
+ return "기타(" + docSts + ")";
+ }
public Map checkApprovalComplete(Map paramMap){
Map resultMap = new HashMap();