diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
index 389ac82..50547d7 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
@@ -24,8 +24,23 @@ $(document).ready(function(){
//날짜
_fnc_datepick();
+ // jQuery 및 select2 로드 확인
+ console.log('jQuery loaded:', typeof $ !== 'undefined');
+ console.log('jQuery.fn exists:', typeof $.fn !== 'undefined');
+ console.log('select2 loaded:', typeof $.fn !== 'undefined' && typeof $.fn.select2 !== 'undefined');
- $('.select2').select2();
+ // select2가 로드되었을 때만 초기화
+ if(typeof $.fn !== 'undefined' && typeof $.fn.select2 === 'function') {
+ $('.select2').select2();
+
+ // 품번/품명 Select2 AJAX 초기화 (common.js의 새 함수 사용)
+ initPartSelect2Ajax("#search_partNo", "#search_partName", "#search_partObjId", {
+ debug: true // 디버깅 모드 활성화
+ });
+ } else {
+ console.error('select2 라이브러리가 로드되지 않았습니다.');
+ console.error('$.fn:', $.fn);
+ }
$("#btnSearch").click(function(){
$("#page").val("1");
@@ -178,7 +193,7 @@ $(document).ready(function(){
// 메일 발송 확인
Swal.fire({
title: '견적서 메일 발송',
- text: "최종 차수의 견적서를 PDF로 발송하시겠습니까?",
+ text: "최종 차수의 견적서를 발송하시겠습니까?",
icon: 'question',
showCancelButton: true,
confirmButtonText: '발송',
@@ -381,6 +396,20 @@ var columns = [
//var grid;
function fn_search(){
+ // Select2 값을 실제 input/select에 명시적으로 설정
+ var partObjId = $("#search_partObjId").val();
+
+ // 디버깅: 검색 조건 확인
+ console.log("품번:", $("#search_partNo").val());
+ console.log("품명:", $("#search_partName").val());
+ console.log("품목 OBJID:", partObjId);
+
+ // 품목 OBJID가 있으면 hidden 필드에 확실히 설정
+ if(partObjId && partObjId !== '') {
+ // 폼 데이터에 명시적으로 추가
+ console.log("품목 검색 조건 설정됨:", partObjId);
+ }
+
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/contractMgmt/contractGridList.do", columns, true);
}
@@ -495,6 +524,7 @@ function fn_showEstimateList(contractObjId){
html += '
견적번호 | ';
html += '작성일 | ';
html += '작성자 | ';
+ html += '결재상태 | ';
html += '';
data.list.forEach(function(item){
@@ -508,6 +538,19 @@ function fn_showEstimateList(contractObjId){
var estimateNo = item.ESTIMATE_NO || item.estimate_no || item.estimateNo || '-';
var regdate = item.REGDATE || item.regdate || '';
var writer = item.WRITER || item.writer || '';
+ var apprStatus = item.APPR_STATUS || item.appr_status || item.apprStatus || '-';
+
+ // 결재상태별 색상 지정
+ var statusColor = '#333';
+ if(apprStatus === '결재완료') {
+ statusColor = 'green';
+ } else if(apprStatus === '결재중') {
+ statusColor = 'blue';
+ } else if(apprStatus === '반려') {
+ statusColor = 'red';
+ } else if(apprStatus === '작성중') {
+ statusColor = '#999';
+ }
html += '';
html += '| ' + revision + '차 | ';
@@ -515,6 +558,7 @@ function fn_showEstimateList(contractObjId){
html += '' + estimateNo + ' | ';
html += '' + regdate + ' | ';
html += '' + writer + ' | ';
+ html += '' + apprStatus + ' | ';
html += '
';
});
@@ -855,18 +899,23 @@ function openProjectFormPopUp(objId){
-
-
- |
-
-
- |
-
-
- |
-
-
- |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
index e13c313..342c1bb 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
@@ -224,6 +224,7 @@ var g_contractObjId = "<%=objId%>";
var g_templateObjId = "<%=templateObjId%>";
var g_exchangeRate = 1; // 환율 (기본값 1)
var g_currencyName = "KRW"; // 통화명 (기본값 원화)
+var g_apprStatus = ""; // 결재상태
$(function(){
@@ -293,6 +294,33 @@ $(function(){
}
});
+// 결재상태에 따라 버튼 표시 제어
+function fn_controlButtons() {
+ if(g_apprStatus === "결재완료") {
+ // 결재완료된 경우 행추가, 저장 버튼 숨김
+ $("#btnAddItem").hide();
+ $("#btnSave").hide();
+
+ // 모든 입력 필드를 읽기 전용으로 변경
+ $("input, textarea").attr("readonly", true);
+ $("input, textarea").css("background-color", "#f5f5f5");
+
+ // 삭제 버튼 숨김
+ $(".btn-delete-row").hide();
+ } else {
+ // 결재완료가 아닌 경우 버튼 표시
+ $("#btnAddItem").show();
+ $("#btnSave").show();
+
+ // 입력 필드 활성화
+ $("input, textarea").attr("readonly", false);
+ $("input, textarea").css("background-color", "");
+
+ // 삭제 버튼 표시
+ $(".btn-delete-row").show();
+ }
+}
+
// 금액 계산
function fn_calculateAmount(row) {
var qty = row.find(".item-qty").val().replace(/,/g, "") || "0";
@@ -376,12 +404,17 @@ function fn_loadData() {
g_exchangeRate = parseFloat(data.estimate.EXCHANGE_RATE || "1");
g_currencyName = data.estimate.CONTRACT_CURRENCY_NAME || "KRW";
- // 데이터 바인딩
- $("#executor").val(data.estimate.EXECUTOR || "");
- $("#recipient").val(data.estimate.RECIPIENT || "");
- $("#estimate_no").val(data.estimate.ESTIMATE_NO || "");
- $("#contact_person").val(data.estimate.CONTACT_PERSON || "");
- $("#greeting_text").val(data.estimate.GREETING_TEXT || "견적을 요청해 주셔서 대단히 감사합니다.\n하기와 같이 견적서를 제출합니다.");
+ // 결재상태 저장
+ g_apprStatus = data.estimate.APPR_STATUS || "작성중";
+
+ // 데이터 바인딩
+ $("#executor").val(data.estimate.EXECUTOR || "");
+ $("#recipient").val(data.estimate.RECIPIENT || "");
+ $("#estimate_no").val(data.estimate.ESTIMATE_NO || "");
+ $("#contact_person").val(data.estimate.CONTACT_PERSON || "");
+ $("#greeting_text").val(data.estimate.GREETING_TEXT || "견적을 요청해 주셔서 대단히 감사합니다.\n하기와 같이 견적서를 제출합니다.");
+ $("#manager_name").val(data.estimate.MANAGER_NAME || "영업부");
+ $("#manager_contact").val(data.estimate.MANAGER_CONTACT || "");
// 품목 데이터 로드
if(data.items && data.items.length > 0) {
@@ -423,18 +456,24 @@ function fn_loadData() {
itemsHtml += '';
itemsHtml += '
';
- $("#itemsTableBody").html(itemsHtml);
-
- // 합계 계산
- fn_calculateTotal();
- }
+ $("#itemsTableBody").html(itemsHtml);
- // 비고 로드
- $("#note1").val(data.estimate.NOTE1 || "1. 견적유효기간: 일");
- $("#note2").val(data.estimate.NOTE2 || "2. 납품기간: 발주 후 1주 이내");
- $("#note3").val(data.estimate.NOTE3 || "3. VAT 별도");
- $("#note4").val(data.estimate.NOTE4 || "4. 결제 조건 : 기존 결제조건에 따름.");
+ // 비고 로드 (테이블 생성 직후)
+ $("#note_remarks").val(data.estimate.NOTE_REMARKS || "");
+
+ // 합계 계산
+ fn_calculateTotal();
}
+
+ // 하단 비고 로드
+ $("#note1").val(data.estimate.NOTE1 || "1. 견적유효기간: 일");
+ $("#note2").val(data.estimate.NOTE2 || "2. 납품기간: 발주 후 1주 이내");
+ $("#note3").val(data.estimate.NOTE3 || "3. VAT 별도");
+ $("#note4").val(data.estimate.NOTE4 || "4. 결제 조건 : 기존 결제조건에 따름.");
+
+ // 결재상태에 따라 버튼 제어
+ fn_controlButtons();
+ }
},
error: function() {
Swal.fire("데이터를 불러오는데 실패했습니다.");
@@ -461,33 +500,45 @@ function fn_loadTemplateData(templateObjId){
g_contractObjId = contractObjId;
}
- // 환율 정보 저장
- g_exchangeRate = parseFloat(template.EXCHANGE_RATE || template.exchange_rate || template.exchangeRate || "1");
- g_currencyName = template.CONTRACT_CURRENCY_NAME || template.contract_currency_name || template.contractCurrencyName || "KRW";
-
- // 대문자/소문자 모두 지원
+ // 환율 정보 저장
+ g_exchangeRate = parseFloat(template.EXCHANGE_RATE || template.exchange_rate || template.exchangeRate || "1");
+ g_currencyName = template.CONTRACT_CURRENCY_NAME || template.contract_currency_name || template.contractCurrencyName || "KRW";
+
+ // 결재상태 저장
+ g_apprStatus = template.APPR_STATUS || template.appr_status || template.apprStatus || "작성중";
+
+ // 대문자/소문자 모두 지원
var executor = template.EXECUTOR || template.executor || "";
var recipient = template.RECIPIENT || template.recipient || "";
var estimateNo = template.ESTIMATE_NO || template.estimate_no || template.estimateNo || "";
var contactPerson = template.CONTACT_PERSON || template.contact_person || template.contactPerson || "";
var greetingText = template.GREETING_TEXT || template.greeting_text || template.greetingText || "";
- var note1 = template.NOTE1 || template.note1 || "";
- var note2 = template.NOTE2 || template.note2 || "";
- var note3 = template.NOTE3 || template.note3 || "";
- var note4 = template.NOTE4 || template.note4 || "";
-
- // 기본 정보 채우기
- $("#executor").val(executor);
- $("#recipient").val(recipient);
- $("#estimate_no").val(estimateNo);
- $("#contact_person").val(contactPerson);
- $("#greeting_text").val(greetingText);
- $("#note1").val(note1);
- $("#note2").val(note2);
- $("#note3").val(note3);
- $("#note4").val(note4);
-
- // 품목 데이터 채우기
+ var note1 = template.NOTE1 || template.note1 || "";
+ var note2 = template.NOTE2 || template.note2 || "";
+ var note3 = template.NOTE3 || template.note3 || "";
+ var note4 = template.NOTE4 || template.note4 || "";
+ var managerName = template.MANAGER_NAME || template.manager_name || template.managerName || "영업부";
+ var managerContact = template.MANAGER_CONTACT || template.manager_contact || template.managerContact || "";
+
+ // 기본 정보 채우기
+ $("#executor").val(executor);
+ $("#recipient").val(recipient);
+ $("#estimate_no").val(estimateNo);
+ $("#contact_person").val(contactPerson);
+ $("#greeting_text").val(greetingText);
+ $("#manager_name").val(managerName);
+ $("#manager_contact").val(managerContact);
+
+ // 하단 비고 로드
+ $("#note1").val(note1);
+ $("#note2").val(note2);
+ $("#note3").val(note3);
+ $("#note4").val(note4);
+
+ // 테이블 내 비고는 나중에 설정 (textarea 생성 후)
+ var noteRemarks = template.NOTE_REMARKS || template.note_remarks || template.noteRemarks || "";
+
+ // 품목 데이터 채우기
if(data.items && data.items.length > 0){
$("#itemsTableBody").empty();
data.items.forEach(function(item, idx){
@@ -530,26 +581,32 @@ function fn_loadTemplateData(templateObjId){
$("#itemsTableBody").append(totalKRWRow);
// 비고 행 추가
- var remarksRow = $("
diff --git a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
index 9e0c037..ffb6178 100644
--- a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp
@@ -27,6 +27,11 @@ $(document).ready(function(){
$('.select2').select2();
+ // 품번/품명 Select2 AJAX 초기화 (common.js의 새 함수 사용)
+ initPartSelect2Ajax("#search_partNo", "#search_partName", "#search_partObjId", {
+ debug: false // 디버깅 모드 비활성화
+ });
+
$("#btnSearch").click(function(){
$("#page").val("1");
fn_search();
@@ -599,18 +604,23 @@ function openProjectFormPopUp(objId){
- |
-
- |
-
-
- |
-
-
- |
-
-
- |
+
+
+ |
+
+
+
+ |
+
+
+ |
+
+
+ |
diff --git a/WebContent/js/common.js b/WebContent/js/common.js
index 52d2fa6..ca10246 100644
--- a/WebContent/js/common.js
+++ b/WebContent/js/common.js
@@ -3255,4 +3255,182 @@ function fnc_tabulCallbackFnc(objid, docType, columnField, fileCnt){
});
}
-//tabulator용 Function 종료
\ No newline at end of file
+//tabulator용 Function 종료
+
+/**
+ * 품번/품명 Select2 AJAX 검색 함수 (견적 목록 전용)
+ * @param {string} partNoSelectId - 품번 셀렉트 박스 ID (예: "#search_partNo")
+ * @param {string} partNameSelectId - 품명 셀렉트 박스 ID (예: "#search_partName")
+ * @param {string} partObjIdInputId - 품목 OBJID hidden 필드 ID (예: "#search_partObjId")
+ * @param {object} options - 추가 옵션 (placeholder, minimumInputLength 등)
+ */
+function initPartSelect2Ajax(partNoSelectId, partNameSelectId, partObjIdInputId, options) {
+ options = options || {};
+ var partNoPlaceholder = options.partNoPlaceholder || "품번 입력하여 검색...";
+ var partNamePlaceholder = options.partNamePlaceholder || "품명 입력하여 검색...";
+ var minimumInputLength = options.minimumInputLength || 1;
+ var searchUrl = options.searchUrl || '/contractMgmt/searchPartList.do';
+ var debug = options.debug || false;
+
+ // 품번 Select2 AJAX 설정
+ $(partNoSelectId).select2({
+ placeholder: partNoPlaceholder,
+ allowClear: true,
+ width: '100%',
+ minimumInputLength: minimumInputLength,
+ language: {
+ inputTooShort: function() {
+ return "최소 " + minimumInputLength + "글자 이상 입력하세요";
+ },
+ searching: function() {
+ return "검색 중...";
+ },
+ noResults: function() {
+ return "검색 결과가 없습니다";
+ }
+ },
+ ajax: {
+ url: searchUrl,
+ dataType: 'json',
+ type: 'POST',
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
+ delay: 250,
+ data: function(params) {
+ return {
+ searchTerm: params.term
+ };
+ },
+ processResults: function(data) {
+ var results = $.map(data, function(item) {
+ var objId = item.OBJID || item.objid || item.objId;
+ var partNo = item.PART_NO || item.part_no || item.partNo;
+ var partName = item.PART_NAME || item.part_name || item.partName;
+
+ return {
+ id: partNo,
+ text: partNo,
+ objId: objId,
+ partName: partName,
+ partNo: partNo
+ };
+ });
+
+ return {
+ results: results
+ };
+ },
+ cache: true
+ }
+ });
+
+ // 품명 Select2 AJAX 설정
+ $(partNameSelectId).select2({
+ placeholder: partNamePlaceholder,
+ allowClear: true,
+ width: '100%',
+ minimumInputLength: minimumInputLength,
+ language: {
+ inputTooShort: function() {
+ return "최소 " + minimumInputLength + "글자 이상 입력하세요";
+ },
+ searching: function() {
+ return "검색 중...";
+ },
+ noResults: function() {
+ return "검색 결과가 없습니다";
+ }
+ },
+ ajax: {
+ url: searchUrl,
+ dataType: 'json',
+ type: 'POST',
+ contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
+ delay: 250,
+ data: function(params) {
+ return {
+ searchTerm: params.term
+ };
+ },
+ processResults: function(data) {
+ var results = $.map(data, function(item) {
+ var objId = item.OBJID || item.objid || item.objId;
+ var partNo = item.PART_NO || item.part_no || item.partNo;
+ var partName = item.PART_NAME || item.part_name || item.partName;
+
+ return {
+ id: partName,
+ text: partName,
+ objId: objId,
+ partName: partName,
+ partNo: partNo
+ };
+ });
+
+ return {
+ results: results
+ };
+ },
+ cache: true
+ }
+ });
+
+ // 품번 변경 이벤트 핸들러
+ var partNoChangeHandler = function() {
+ var selectedData = $(this).select2('data')[0];
+ if(debug) console.log("품번 변경됨:", selectedData);
+
+ if(selectedData && selectedData.objId) {
+ if(debug) console.log("품목 OBJID 설정:", selectedData.objId);
+ $(partObjIdInputId).val(selectedData.objId);
+
+ // 품명 셀렉트박스도 동기화
+ var $partNameSelect = $(partNameSelectId);
+ $partNameSelect.off('change');
+ if($partNameSelect.find("option[value='" + selectedData.partName + "']").length === 0) {
+ var newOption = new Option(selectedData.partName, selectedData.partName, true, true);
+ $partNameSelect.append(newOption);
+ } else {
+ $partNameSelect.val(selectedData.partName);
+ }
+ $partNameSelect.trigger('change.select2');
+
+ setTimeout(function() {
+ $partNameSelect.on('change', partNameChangeHandler);
+ }, 100);
+ } else {
+ $(partObjIdInputId).val("");
+ }
+ };
+
+ // 품명 변경 이벤트 핸들러
+ var partNameChangeHandler = function() {
+ var selectedData = $(this).select2('data')[0];
+ if(debug) console.log("품명 변경됨:", selectedData);
+
+ if(selectedData && selectedData.objId) {
+ if(debug) console.log("품목 OBJID 설정:", selectedData.objId);
+ $(partObjIdInputId).val(selectedData.objId);
+
+ // 품번 셀렉트박스도 동기화
+ var $partNoSelect = $(partNoSelectId);
+ $partNoSelect.off('change');
+ if($partNoSelect.find("option[value='" + selectedData.partNo + "']").length === 0) {
+ var newOption = new Option(selectedData.partNo, selectedData.partNo, true, true);
+ $partNoSelect.append(newOption);
+ } else {
+ $partNoSelect.val(selectedData.partNo);
+ }
+ $partNoSelect.trigger('change.select2');
+
+ setTimeout(function() {
+ $partNoSelect.on('change', partNoChangeHandler);
+ }, 100);
+ } else {
+ $(partObjIdInputId).val("");
+ }
+ };
+
+ // 이벤트 연결
+ $(partNoSelectId).on('change', partNoChangeHandler);
+ $(partNameSelectId).on('change', partNameChangeHandler);
+}
\ No newline at end of file
diff --git a/database/add_manager_info_to_estimate_template.sql b/database/add_manager_info_to_estimate_template.sql
new file mode 100644
index 0000000..1632c2f
--- /dev/null
+++ b/database/add_manager_info_to_estimate_template.sql
@@ -0,0 +1,16 @@
+-- ESTIMATE_TEMPLATE 테이블에 담당자 정보 및 테이블 내 비고 컬럼 추가
+
+ALTER TABLE ESTIMATE_TEMPLATE
+ADD COLUMN IF NOT EXISTS MANAGER_NAME VARCHAR(100);
+
+ALTER TABLE ESTIMATE_TEMPLATE
+ADD COLUMN IF NOT EXISTS MANAGER_CONTACT VARCHAR(100);
+
+ALTER TABLE ESTIMATE_TEMPLATE
+ADD COLUMN IF NOT EXISTS NOTE_REMARKS TEXT;
+
+-- 컬럼 설명 추가
+COMMENT ON COLUMN ESTIMATE_TEMPLATE.MANAGER_NAME IS '담당자 이름';
+COMMENT ON COLUMN ESTIMATE_TEMPLATE.MANAGER_CONTACT IS '담당자 연락처';
+COMMENT ON COLUMN ESTIMATE_TEMPLATE.NOTE_REMARKS IS '테이블 내 비고 (품목 하단)';
+
diff --git a/database/add_part_objid_to_project_mgmt.sql b/database/add_part_objid_to_project_mgmt.sql
new file mode 100644
index 0000000..c24d7f8
--- /dev/null
+++ b/database/add_part_objid_to_project_mgmt.sql
@@ -0,0 +1,27 @@
+-- PROJECT_MGMT 테이블에 품목 관련 컬럼 추가
+-- 품목별 프로젝트 생성을 위한 컬럼들
+
+ALTER TABLE PROJECT_MGMT
+ADD COLUMN IF NOT EXISTS PART_OBJID VARCHAR(50);
+
+ALTER TABLE PROJECT_MGMT
+ADD COLUMN IF NOT EXISTS PART_NO VARCHAR(100);
+
+ALTER TABLE PROJECT_MGMT
+ADD COLUMN IF NOT EXISTS PART_NAME VARCHAR(200);
+
+ALTER TABLE PROJECT_MGMT
+ADD COLUMN IF NOT EXISTS QUANTITY VARCHAR(50);
+
+-- 컬럼 설명 추가
+COMMENT ON COLUMN PROJECT_MGMT.PART_OBJID IS '품목 OBJID (품목별 프로젝트 생성 시 사용)';
+COMMENT ON COLUMN PROJECT_MGMT.PART_NO IS '품번';
+COMMENT ON COLUMN PROJECT_MGMT.PART_NAME IS '품명';
+COMMENT ON COLUMN PROJECT_MGMT.QUANTITY IS '수주수량';
+
+-- 기존 인덱스 확인 후 추가 (성능 향상)
+CREATE INDEX IF NOT EXISTS idx_project_mgmt_contract_part
+ON PROJECT_MGMT(CONTRACT_OBJID, PART_OBJID);
+
+COMMENT ON INDEX idx_project_mgmt_contract_part IS '계약-품목별 프로젝트 조회 성능 향상';
+
diff --git a/src/com/pms/common/utils/MailUtil.java b/src/com/pms/common/utils/MailUtil.java
index 0720cfe..ac05603 100644
--- a/src/com/pms/common/utils/MailUtil.java
+++ b/src/com/pms/common/utils/MailUtil.java
@@ -691,6 +691,7 @@ public class MailUtil {
//◆◆◆ 3. db log & send mail ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆
SqlSession sqlSession = null;
+ boolean mailSendSuccess = false; // 메일 발송 성공 여부
Map paramMap = new HashMap();
try{
@@ -715,6 +716,7 @@ public class MailUtil {
//◆◆◆ send mail ◆◆◆
Transport.send(message);
+ mailSendSuccess = true; // 메일 발송 성공
if(Constants.Mail.dbLogWrite){
System.out.println("메일 발송후 paramMap >> "+paramMap);
@@ -722,6 +724,7 @@ public class MailUtil {
}
}catch(Exception sqle){
+ mailSendSuccess = false; // 메일 발송 실패
if(Constants.Mail.dbLogWrite){
paramMap.put("errorLog", sqle.getMessage());
System.out.println("메일 발송 오류 paramMap >> "+paramMap);
@@ -735,7 +738,7 @@ public class MailUtil {
}
}
- return true;
+ return mailSendSuccess; // 실제 발송 성공 여부 반환
}catch(Exception e) {
e.printStackTrace();
return false;
diff --git a/src/com/pms/mapper/project.xml b/src/com/pms/mapper/project.xml
index 65bda68..439bf4d 100644
--- a/src/com/pms/mapper/project.xml
+++ b/src/com/pms/mapper/project.xml
@@ -7362,16 +7362,20 @@ SELECT
,EST_USER_ID
,EST_COMP_DATE
,EST_RESULT_CD
- ,AREA_CD
- ,MECHANICAL_TYPE
- ,OVERHAUL_ORDER
- ,IS_TEMP
- )
+ ,AREA_CD
+ ,MECHANICAL_TYPE
+ ,OVERHAUL_ORDER
+ ,IS_TEMP
+ ,PART_OBJID
+ ,PART_NO
+ ,PART_NAME
+ ,QUANTITY
+ )
(
SELECT
#{OBJID}
- ,#{objId}
+ ,#{objId}
,CATEGORY_CD
,CUSTOMER_OBJID
,PRODUCT
@@ -7390,29 +7394,71 @@ SELECT
,CHG_USER_ID
,PLAN_DATE
,COMPLETE_DATE
- ,RESULT_CD
-
-
- ,#{overhaul_project_no}
-
-
- ,MECHANICAL_TYPE || '-' ||
-
- (SELECT CASE
- WHEN PROJECT_NO ~ '[-\s][0-9]+$'
- THEN REGEXP_REPLACE(PROJECT_NO, '.*[-\s]([0-9]+)$', '\1')::integer+1
- ELSE NULL
- END AS extracted_number
- FROM PROJECT_MGMT
- WHERE PROJECT_NO NOT LIKE '%\_%' ESCAPE '\'
- ORDER BY extracted_number DESC NULLS LAST
- LIMIT 1)
-
-
-
-
+ ,RESULT_CD
+
+
+
+ ,(
+ SELECT
+ -- 주문유형 코드 (CATEGORY_CD를 영문 약어로 매핑)
+ CASE CODE_NAME(CATEGORY_CD)
+ WHEN '오버홀' THEN 'O'
+ WHEN '개조' THEN 'M'
+ WHEN '개발' THEN 'D'
+ WHEN '견적' THEN 'Q'
+ WHEN '수리' THEN 'R'
+ WHEN '판매' THEN 'S'
+ ELSE 'T'
+ END || '-' ||
+ -- 제품구분 코드 (PRODUCT의 CODE_NAME에서 슬래시 제거)
+ REPLACE(CODE_NAME(PRODUCT), '/', '') || '-' ||
+ -- 날짜 (YYMMDD)
+ TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-' ||
+ -- 순번 (001, 002, ...)
+ LPAD(
+ COALESCE(
+ (
+ SELECT MAX(SUBSTRING(PROJECT_NO FROM '\d{3}$')::INTEGER) + 1
+ FROM PROJECT_MGMT
+ WHERE PROJECT_NO LIKE
+ CASE CODE_NAME(CATEGORY_CD)
+ WHEN '오버홀' THEN 'O'
+ WHEN '개조' THEN 'M'
+ WHEN '개발' THEN 'D'
+ WHEN '견적' THEN 'Q'
+ WHEN '수리' THEN 'R'
+ WHEN '판매' THEN 'S'
+ ELSE 'T'
+ END || '-' ||
+ REPLACE(CODE_NAME(PRODUCT), '/', '') || '-' ||
+ TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-%'
+ ),
+ 1
+ )::TEXT,
+ 3,
+ '0'
+ )
+ FROM CONTRACT_MGMT
+ WHERE OBJID = #{objId}
+ )
,PM_USER_ID
,#{contract_price}
,#{contract_price_currency}
@@ -7437,12 +7483,16 @@ SELECT
,EST_USER_ID
,EST_COMP_DATE
,EST_RESULT_CD
- ,AREA_CD
- ,MECHANICAL_TYPE
- ,#{overhaul_order}
- ,#{is_temp}
- FROM CONTRACT_MGMT
- WHERE OBJID=#{objId}
+ ,AREA_CD
+ ,MECHANICAL_TYPE
+ ,#{overhaul_order}
+ ,#{is_temp}
+ ,#{part_objid}
+ ,#{part_no}
+ ,#{part_name}
+ ,#{quantity}
+ FROM CONTRACT_MGMT
+ WHERE OBJID=#{objId}
)
@@ -7549,7 +7599,12 @@ SELECT
,REQ_DEL_DATE = #{req_del_date}
,CONTRACT_COMPANY = #{contract_company}
,MANUFACTURE_PLANT = #{manufacture_plant}
+ ,PART_OBJID = #{part_objid}
+ ,PART_NO = #{part_no}
+ ,PART_NAME = #{part_name}
+ ,QUANTITY = #{quantity}
WHERE CONTRACT_OBJID = #{objId}
+ AND PART_OBJID = #{part_objid}
diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
index 7e796db..844c376 100644
--- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml
+++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
@@ -463,8 +463,9 @@
,TO_CHAR(REGDATE,'YYYY-MM-DD') AS REG_DATE
,WRITER
,(SELECT USER_NAME FROM USER_INFO AS O WHERE O.USER_ID = T.WRITER ) AS WRITER_NAME
- ,(SELECT COUNT(1) FROM ATTACH_FILE_INFO WHERE TARGET_OBJID = T.OBJID AND DOC_TYPE='contractMgmt01' AND UPPER(STATUS) = 'ACTIVE') AS CU01_CNT
- ,(SELECT COUNT(1) FROM ATTACH_FILE_INFO WHERE TARGET_OBJID = T.OBJID AND DOC_TYPE='contractMgmt02' AND UPPER(STATUS) = 'ACTIVE') AS CU02_CNT
+ ,(SELECT COUNT(1) FROM ATTACH_FILE_INFO WHERE TARGET_OBJID = T.OBJID AND DOC_TYPE='ORDER_DOC' AND UPPER(STATUS) = 'ACTIVE') AS CU01_CNT
+
,(CASE WHEN (RESULT_CD is null or RESULT_CD ='') and (SPEC_RESULT_CD is null or RESULT_CD ='') and (EST_RESULT_CD is null or RESULT_CD ='') then '0'
ELSE 1
END
@@ -477,6 +478,7 @@
,CODE_NAME(CONTRACT_COMPANY) AS CONTRACT_COMPANY_NAME
,CONTRACT_DATE
,PO_NO
+ ,ORDER_DATE
,MANUFACTURE_PLANT
,CODE_NAME(MANUFACTURE_PLANT) AS MANUFACTURE_PLANT_NAME
,CONTRACT_RESULT
@@ -753,12 +755,15 @@
AND PAID_TYPE = #{paid_type}
-
- AND UPPER(PART_NO) LIKE UPPER('%${search_partNo}%')
-
-
-
- AND UPPER(PART_NAME) LIKE UPPER('%${search_partName}%')
+
+
+ AND EXISTS (
+ SELECT 1
+ FROM CONTRACT_ITEM CI
+ WHERE CI.CONTRACT_OBJID = T.OBJID
+ AND CI.STATUS = 'ACTIVE'
+ AND CI.PART_OBJID = #{search_partObjId}
+ )
@@ -779,9 +784,9 @@
AND TO_DATE(DUE_DATE,'YYYY-MM-DD') TO_DATE(#{due_end_date}, 'YYYY-MM-DD')
ORDER BY REGDATE DESC
-
-
-
+
+
+
+
+
@@ -3886,14 +3939,32 @@ ORDER BY ASM.SUPPLY_NAME
ET.NOTE2,
ET.NOTE3,
ET.NOTE4,
+ ET.NOTE_REMARKS,
ET.TOTAL_AMOUNT,
ET.TOTAL_AMOUNT_KRW,
+ ET.MANAGER_NAME,
+ ET.MANAGER_CONTACT,
ET.WRITER,
TO_CHAR(ET.REGDATE, 'YYYY-MM-DD HH24:MI') AS REGDATE,
ET.CHG_USER_ID,
TO_CHAR(ET.CHGDATE, 'YYYY-MM-DD HH24:MI') AS CHGDATE,
CM.EXCHANGE_RATE,
- CODE_NAME(CM.CONTRACT_CURRENCY) AS CONTRACT_CURRENCY_NAME
+ CODE_NAME(CM.CONTRACT_CURRENCY) AS CONTRACT_CURRENCY_NAME,
+ COALESCE(
+ (SELECT CASE
+ WHEN A.STATUS = 'complete' THEN '결재완료'
+ WHEN A.STATUS = 'cancel' THEN '취소'
+ WHEN A.STATUS = 'reject' THEN '반려'
+ WHEN A.STATUS = 'inProcess' THEN '결재중'
+ ELSE '작성중'
+ END
+ FROM APPROVAL A
+ WHERE A.TARGET_OBJID::VARCHAR = ET.OBJID
+ AND A.TARGET_TYPE = 'CONTRACT_ESTIMATE'
+ ORDER BY A.REGDATE DESC
+ LIMIT 1),
+ '작성중'
+ ) AS APPR_STATUS
FROM
ESTIMATE_TEMPLATE ET
LEFT JOIN CONTRACT_MGMT CM ON ET.CONTRACT_OBJID = CM.OBJID
@@ -3963,39 +4034,45 @@ ORDER BY ASM.SUPPLY_NAME
MODEL_NAME,
MODEL_CODE,
EXECUTOR_DATE,
- NOTE1,
- NOTE2,
- NOTE3,
- NOTE4,
- TOTAL_AMOUNT,
- TOTAL_AMOUNT_KRW,
- WRITER,
- REGDATE,
- CHG_USER_ID,
- CHGDATE
- ) VALUES (
- #{template_objid},
- #{objId},
- #{template_type},
- #{executor},
- #{recipient},
- #{estimate_no},
- #{contact_person},
- #{greeting_text},
- #{model_name},
- #{model_code},
- #{executor_date},
- #{note1},
- #{note2},
- #{note3},
- #{note4},
- #{total_amount},
- #{total_amount_krw},
- #{writer},
- NOW(),
- #{chg_user_id},
- NOW()
- )
+ NOTE1,
+ NOTE2,
+ NOTE3,
+ NOTE4,
+ NOTE_REMARKS,
+ TOTAL_AMOUNT,
+ TOTAL_AMOUNT_KRW,
+ MANAGER_NAME,
+ MANAGER_CONTACT,
+ WRITER,
+ REGDATE,
+ CHG_USER_ID,
+ CHGDATE
+) VALUES (
+ #{template_objid},
+ #{objId},
+ #{template_type},
+ #{executor},
+ #{recipient},
+ #{estimate_no},
+ #{contact_person},
+ #{greeting_text},
+ #{model_name},
+ #{model_code},
+ #{executor_date},
+ #{note1},
+ #{note2},
+ #{note3},
+ #{note4},
+ #{note_remarks},
+ #{total_amount},
+ #{total_amount_krw},
+ #{manager_name},
+ #{manager_contact},
+ #{writer},
+ NOW(),
+ #{chg_user_id},
+ NOW()
+)
@@ -4010,16 +4087,19 @@ ORDER BY ASM.SUPPLY_NAME
MODEL_NAME = #{model_name},
MODEL_CODE = #{model_code},
EXECUTOR_DATE = #{executor_date},
- NOTE1 = #{note1},
- NOTE2 = #{note2},
- NOTE3 = #{note3},
- NOTE4 = #{note4},
- TOTAL_AMOUNT = #{total_amount},
- TOTAL_AMOUNT_KRW = #{total_amount_krw},
- CHG_USER_ID = #{chg_user_id},
- CHGDATE = NOW()
- WHERE
- OBJID = #{template_objid}
+ NOTE1 = #{note1},
+ NOTE2 = #{note2},
+ NOTE3 = #{note3},
+ NOTE4 = #{note4},
+ NOTE_REMARKS = #{note_remarks},
+ TOTAL_AMOUNT = #{total_amount},
+ TOTAL_AMOUNT_KRW = #{total_amount_krw},
+ MANAGER_NAME = #{manager_name},
+ MANAGER_CONTACT = #{manager_contact},
+ CHG_USER_ID = #{chg_user_id},
+ CHGDATE = NOW()
+WHERE
+ OBJID = #{template_objid}
@@ -4095,8 +4175,11 @@ ORDER BY ASM.SUPPLY_NAME
ET.NOTE2 AS "NOTE2",
ET.NOTE3 AS "NOTE3",
ET.NOTE4 AS "NOTE4",
+ ET.NOTE_REMARKS AS "NOTE_REMARKS",
ET.TOTAL_AMOUNT AS "TOTAL_AMOUNT",
ET.TOTAL_AMOUNT_KRW AS "TOTAL_AMOUNT_KRW",
+ ET.MANAGER_NAME AS "MANAGER_NAME",
+ ET.MANAGER_CONTACT AS "MANAGER_CONTACT",
ET.WRITER AS "WRITER",
TO_CHAR(ET.REGDATE, 'YYYY-MM-DD HH24:MI') AS "REGDATE",
ET.CHG_USER_ID AS "CHG_USER_ID",
@@ -4104,11 +4187,11 @@ ORDER BY ASM.SUPPLY_NAME
ET.CATEGORIES_JSON AS "CATEGORIES_JSON",
CM.EXCHANGE_RATE AS "EXCHANGE_RATE",
CODE_NAME(CM.CONTRACT_CURRENCY) AS "CONTRACT_CURRENCY_NAME"
- FROM
- ESTIMATE_TEMPLATE ET
- LEFT JOIN CONTRACT_MGMT CM ON ET.CONTRACT_OBJID = CM.OBJID
- WHERE
- ET.CONTRACT_OBJID = #{objId}
+FROM
+ ESTIMATE_TEMPLATE ET
+LEFT JOIN CONTRACT_MGMT CM ON ET.CONTRACT_OBJID = CM.OBJID
+WHERE
+ ET.CONTRACT_OBJID = #{objId}
ORDER BY
ET.TEMPLATE_TYPE,
ET.REGDATE DESC
diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java
index 323cb51..820df4c 100644
--- a/src/com/pms/salesmgmt/service/ContractMgmtService.java
+++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java
@@ -1621,18 +1621,24 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
contents.append("");
contents.append("");
contents.append("");
@@ -1656,14 +1662,77 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
String executorDate = CommonUtils.checkNull(estimateTemplate.get("executor_date"));
if("".equals(executorDate)) executorDate = CommonUtils.checkNull(estimateTemplate.get("EXECUTOR_DATE"));
- // 기본 정보 테이블
+ // 담당자 정보
+ String managerName = CommonUtils.checkNull(estimateTemplate.get("manager_name"));
+ if("".equals(managerName)) managerName = CommonUtils.checkNull(estimateTemplate.get("MANAGER_NAME"));
+ if("".equals(managerName)) managerName = "영업부";
+
+ String managerContact = CommonUtils.checkNull(estimateTemplate.get("manager_contact"));
+ if("".equals(managerContact)) managerContact = CommonUtils.checkNull(estimateTemplate.get("MANAGER_CONTACT"));
+
+ // 헤더 섹션 (왼쪽: 기본정보, 오른쪽: 회사정보)
+ contents.append(""); // header-section 닫기
// 견적 품목
if(estimateItems != null && !estimateItems.isEmpty()){
@@ -1704,17 +1773,51 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
contents.append("
");
}
- // 합계
+ // 계 (총 합계)
+ String totalAmountStr = CommonUtils.checkNull(estimateTemplate.get("total_amount"));
+ if("".equals(totalAmountStr)) totalAmountStr = CommonUtils.checkNull(estimateTemplate.get("TOTAL_AMOUNT"));
+
+ // DB에 저장된 합계가 없으면 계산한 값 사용
+ if("".equals(totalAmountStr)){
+ totalAmountStr = String.format("%,d", totalAmount);
+ }
+
contents.append("");
- contents.append("| 합계 | ");
- contents.append("₩" + String.format("%,d", totalAmount) + " | ");
+ contents.append("계 | ");
+ contents.append("" + totalAmountStr + " | ");
contents.append(" | ");
contents.append("
");
+
+ // 원화환산 공급가액
+ String totalAmountKrw = CommonUtils.checkNull(estimateTemplate.get("total_amount_krw"));
+ if("".equals(totalAmountKrw)) totalAmountKrw = CommonUtils.checkNull(estimateTemplate.get("TOTAL_AMOUNT_KRW"));
+
+ if(!"".equals(totalAmountKrw)){
+ contents.append("");
+ contents.append("| 원화환산 공급가액 (KRW) | ");
+ contents.append("" + totalAmountKrw + " | ");
+ contents.append(" | ");
+ contents.append("
");
+ }
+
+ // 테이블 내 비고
+ String noteRemarks = CommonUtils.checkNull(estimateTemplate.get("note_remarks"));
+ if("".equals(noteRemarks)) noteRemarks = CommonUtils.checkNull(estimateTemplate.get("NOTE_REMARKS"));
+
+ if(!"".equals(noteRemarks)){
+ contents.append("");
+ contents.append("| ");
+ contents.append(" <비고> ");
+ contents.append("" + noteRemarks + " ");
+ contents.append(" | ");
+ contents.append("
");
+ }
+
contents.append("");
contents.append("");
}
- // 비고 (대소문자 모두 체크)
+ // 참조사항 (NOTE1~4)
String note1 = CommonUtils.checkNull(estimateTemplate.get("note1"));
if("".equals(note1)) note1 = CommonUtils.checkNull(estimateTemplate.get("NOTE1"));
@@ -1729,11 +1832,23 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
if(!"".equals(note1) || !"".equals(note2) || !"".equals(note3) || !"".equals(note4)){
contents.append("");
- contents.append("
※ 비고
");
- if(!"".equals(note1)) contents.append("
1. " + note1 + "
");
- if(!"".equals(note2)) contents.append("
2. " + note2 + "
");
- if(!"".equals(note3)) contents.append("
3. " + note3 + "
");
- if(!"".equals(note4)) contents.append("
4. " + note4 + "
");
+ contents.append("
※ 참조사항
");
+ int noteNum = 1;
+ if(!"".equals(note1)) {
+ contents.append("
" + noteNum + ". " + note1 + "
");
+ noteNum++;
+ }
+ if(!"".equals(note2)) {
+ contents.append("
" + noteNum + ". " + note2 + "
");
+ noteNum++;
+ }
+ if(!"".equals(note3)) {
+ contents.append("
" + noteNum + ". " + note3 + "
");
+ noteNum++;
+ }
+ if(!"".equals(note4)) {
+ contents.append("
" + noteNum + ". " + note4 + "
");
+ }
contents.append("
");
}
@@ -1744,6 +1859,42 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
return contents.toString();
}
+/**
+ * 이미지 파일을 Base64로 인코딩
+ * @param imagePath 이미지 파일 경로
+ * @return Base64 인코딩된 문자열 (data:image/png;base64,...)
+ */
+private String encodeImageToBase64(String imagePath) {
+ try {
+ File imageFile = new File(imagePath);
+ if(!imageFile.exists()) {
+ return "";
+ }
+
+ java.io.FileInputStream fis = new java.io.FileInputStream(imageFile);
+ byte[] imageBytes = new byte[(int) imageFile.length()];
+ fis.read(imageBytes);
+ fis.close();
+
+ // Apache Commons Codec 사용 (Java 7 호환)
+ String base64 = org.apache.commons.codec.binary.Base64.encodeBase64String(imageBytes);
+
+ // 파일 확장자로 MIME 타입 결정
+ String mimeType = "image/png";
+ if(imagePath.toLowerCase().endsWith(".jpg") || imagePath.toLowerCase().endsWith(".jpeg")) {
+ mimeType = "image/jpeg";
+ } else if(imagePath.toLowerCase().endsWith(".gif")) {
+ mimeType = "image/gif";
+ }
+
+ return "data:" + mimeType + ";base64," + base64;
+ } catch(Exception e) {
+ System.out.println("이미지 Base64 인코딩 실패: " + e.getMessage());
+ e.printStackTrace();
+ return "";
+ }
+}
+
/**
* 영업정보 조회 (수주등록용)
* @param contractObjId
@@ -1907,51 +2058,96 @@ private String makeEstimateMailContents(Map contractInfo, Map estimateTemplate,
//paramMap.put("contract_price_currency", contract_price_currency/project_cnt + "");
//paramMap.put("contract_price", contract_price/project_cnt + "");
- if("0000964".equals(result_cd) || "0000968".equals(result_cd)){
- resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap);
- System.out.println("resultList:::"+resultList);
- //resultList = sqlSession.selectOne("contractMgmt.getProjectCnt", paramMap);
- if(null==resultList){
-
-// for (int i=0; i contractItems = getContractItems(paramMap);
+
+ if(contractItems != null && !contractItems.isEmpty()) {
+ System.out.println("품목 개수: " + contractItems.size() + "개 - 품목별 프로젝트 생성 시작");
+
+ for(Map item : contractItems) {
+ // 품목별 프로젝트 존재 여부 확인
+ Map projectCheckParam = new HashMap();
+ projectCheckParam.put("contractObjId", contract_objid);
+ projectCheckParam.put("part_objid", item.get("PART_OBJID"));
+
+ resultList = sqlSession.selectOne("contractMgmt.getProjectListByContractAndPartObjid", projectCheckParam);
+
+ if(null == resultList) {
+ // 새 프로젝트 생성
+ Map projectParam = new HashMap();
+ projectParam.putAll(paramMap); // 기본 정보 복사
+
+ // 품목별 정보 설정
+ projectParam.put("OBJID", CommonUtils.createObjId());
+ projectParam.put("is_temp", '1');
+ projectParam.put("part_objid", item.get("PART_OBJID"));
+ projectParam.put("part_no", item.get("PART_NO"));
+ projectParam.put("part_name", item.get("PART_NAME"));
+ projectParam.put("quantity", item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"));
+ projectParam.put("due_date", item.get("DUE_DATE"));
- paramMap.put("OBJID", CommonUtils.createObjId());
- paramMap.put("is_temp", '1');
- //paramMap.put("facility_qty", '1');
if("0000170".equals(category_cd) || "0000171".equals(category_cd)){
- paramMap.put("overhaul_project_no", target_project_no);
- //paramMap.put("overhaul_order", overhaul_order+i);
- }else{
+ projectParam.put("overhaul_project_no", target_project_no);
}
- //프로젝트 등록
- cnt = sqlSession.update("project.createProject", paramMap);
- //프로젝트 TASK 등록
- cnt = sqlSession.insert("contractMgmt.insertProjectTask", paramMap);
- //프로젝트 SETUP_TASK 등록
- cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap);
- //project_no - unit 폴더 생성
- //paramMap.put("OBJID", paramMap.get("OBJID"));
- Map projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", paramMap);
-
- paramMap.put("contract_objid", paramMap.get("contractObjId"));
- paramMap.put("customer_product", paramMap.get("mechanical_type"));
- List