diff --git a/.gitignore b/.gitignore
index cd16182..431f5cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,9 @@ Thumbs.db
# Cursor files
.cursor/
+
+# Claude Code
+CLAUDE.md
+.claude/
+.playwright-mcp/
+.omc/
diff --git a/WebContent/WEB-INF/view/contractMgmt/addEstimatePdfPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/addEstimatePdfPopup.jsp
new file mode 100644
index 0000000..8661349
--- /dev/null
+++ b/WebContent/WEB-INF/view/contractMgmt/addEstimatePdfPopup.jsp
@@ -0,0 +1,150 @@
+<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
+<%@ page import="com.pms.common.utils.*"%>
+<%@ page import="java.util.*" %>
+<%@include file= "/init.jsp" %>
+
+
+
+
+<%=Constants.SYSTEM_NAME%>
+
+
+
+
+
+
+
diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
index 2fdfb32..c927c8d 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp
@@ -74,7 +74,7 @@
$(this).val(fnc_addComma($(this).val().replace(/[^0-9.]/g, "")));
}
});
- $("input:text[numberOnly]").on("blur", function() {
+ $(document).on("blur", "input:text[numberOnly]", function() {
var val = $(this).val();
if(val && val !== '') {
if($(this).hasClass("item-order-quantity") || $(this).attr("id") === "facility_qty") {
@@ -133,6 +133,13 @@
function addComma(data) {
return formatMoney(data);
}
+
+ function addCommaInt(data) {
+ if(!data && data !== 0) return '';
+ var num = Math.round(Number(String(data).replace(/,/g, '')));
+ if(isNaN(num)) return '';
+ return num.toLocaleString();
+ }
function removeComma(data) {
if(!data) return '';
@@ -331,30 +338,30 @@
// 수주수량 (Machine이고 프로젝트가 있으면 readonly)
html += '';
if(isMachine && hasProject) {
- html += '';
+ html += '';
} else {
- html += '';
+ html += '';
}
html += ' | ';
-
+
// 수주단가
html += '';
- html += '';
+ html += '';
html += ' | ';
-
+
// 수주공급가액 (자동계산 + 수정가능)
html += '';
- html += '';
+ html += '';
html += ' | ';
-
+
// 수주부가세 (자동계산 + 수정가능)
html += '';
- html += '';
+ html += '';
html += ' | ';
-
+
// 수주총액 (자동계산 + 수정가능)
html += '';
- html += '';
+ html += '';
html += ' | ';
// 삭제 버튼
@@ -401,7 +408,8 @@
} else if($(this).hasClass("item-order-vat")) {
fn_calculateTotalFromVat(itemId);
}
- // 총액 직접 수정시에는 재계산 안함
+ // 총액 직접 수정시에도 합계는 갱신
+ fn_calculateTotal();
});
// 품목 정보 저장
@@ -543,22 +551,22 @@
// 수주 정보 (Machine이고 프로젝트가 있으면 수량 readonly)
html += '';
if(isMachine && hasProject) {
- html += '';
+ html += '';
} else {
- html += '';
+ html += '';
}
html += ' | ';
html += '';
- html += '';
+ html += '';
html += ' | ';
html += '';
- html += '';
+ html += '';
html += ' | ';
html += '';
- html += '';
+ html += '';
html += ' | ';
html += '';
- html += '';
+ html += '';
html += ' | ';
html += '';
@@ -624,6 +632,8 @@
} else if($(this).hasClass("item-order-vat")) {
fn_calculateTotalFromVat(itemId);
}
+ // 총액 직접 수정시에도 합계는 갱신
+ fn_calculateTotal();
});
// 품목 정보 저장
@@ -636,9 +646,11 @@
}
}
%>
+ // 기존 품목 로드 완료 후 합계 계산
+ fn_calculateTotal();
}
-
- // 품목별 금액 계산
+
+ // 품목별 금액 계산
function fn_calculateItemAmount(itemId) {
var quantity = parseFloat(removeComma($("#" + itemId + " .item-order-quantity").val())) || 0;
var unitPrice = parseFloat(removeComma($("#" + itemId + " .item-order-unit-price").val())) || 0;
@@ -654,8 +666,9 @@
// 총액 계산
var totalAmount = supplyPrice + vat;
$("#" + itemId + " .item-order-total-amount").val(addComma(totalAmount));
+ fn_calculateTotal();
}
-
+
// 부가세 직접 입력 시 총액만 재계산
function fn_calculateTotalFromVat(itemId) {
var supplyPrice = parseFloat(removeComma($("#" + itemId + " .item-order-supply-price").val())) || 0;
@@ -664,19 +677,36 @@
// 총액 계산
var totalAmount = supplyPrice + vat;
$("#" + itemId + " .item-order-total-amount").val(addComma(totalAmount));
+ fn_calculateTotal();
}
-
+
// 공급가액 직접 입력 시 부가세와 총액 재계산
function fn_calculateFromSupplyPrice(itemId) {
var supplyPrice = parseFloat(removeComma($("#" + itemId + " .item-order-supply-price").val())) || 0;
-
+
// 부가세 자동 계산 (공급가액의 10%)
var vat = Math.round(supplyPrice * 0.1);
$("#" + itemId + " .item-order-vat").val(addComma(vat));
-
+
// 총액 계산
var totalAmount = supplyPrice + vat;
$("#" + itemId + " .item-order-total-amount").val(addComma(totalAmount));
+ fn_calculateTotal();
+ }
+
+ // 품목 합계 계산
+ function fn_calculateTotal() {
+ var totalQty = 0, totalSupply = 0, totalVat = 0, totalAmount = 0;
+ $(".item-row").each(function() {
+ totalQty += parseFloat(removeComma($(this).find(".item-order-quantity").val())) || 0;
+ totalSupply += parseFloat(removeComma($(this).find(".item-order-supply-price").val())) || 0;
+ totalVat += parseFloat(removeComma($(this).find(".item-order-vat").val())) || 0;
+ totalAmount += parseFloat(removeComma($(this).find(".item-order-total-amount").val())) || 0;
+ });
+ $("#totalOrderQuantity").text(addCommaInt(totalQty));
+ $("#totalOrderSupplyPrice").text(addComma(totalSupply));
+ $("#totalOrderVat").text(addComma(totalVat));
+ $("#totalOrderTotalAmount").text(addComma(totalAmount));
}
// 품번/품명 셀렉트박스 옵션 채우기 (Select2 AJAX)
@@ -1037,7 +1067,7 @@
if(confirm("해당 품목을 삭제하시겠습니까?")) {
$("#" + itemId).remove();
-
+
// itemList에서 제거
for(var i = 0; i < itemList.length; i++) {
if(itemList[i].id == itemId) {
@@ -1045,10 +1075,13 @@
break;
}
}
-
+
// 행 번호 재정렬
fn_reorderItemRows();
-
+
+ // 합계 재계산
+ fn_calculateTotal();
+
// 모든 행이 삭제되면 안내 메시지 표시
if($(".item-row").length == 0) {
$("#noItemRow").show();
@@ -1487,6 +1520,17 @@
|
+
+
+ | Total |
+ 0 |
+ |
+ 0.00 |
+ 0.00 |
+ 0.00 |
+ |
+
+
diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
index a7a0369..8ac36df 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateList_new.jsp
@@ -414,7 +414,22 @@ var columns = [
var objid = fnc_checkNull(cell.getData().OBJID);
fn_showEstimateList(objid);
}
- },
+ },
+ // 11-1. 추가견적 PDF 첨부
+ {headerHozAlign : 'center', hozAlign : 'center', minWidth: 55, widthGrow: 0.7, title : '추가견적', field : 'ADD_EST_CNT',
+ formatter: function(cell, formatterParams, onRendered){
+ var cnt = fnc_checkNull(cell.getValue());
+ var icon = '📎';
+ if(cnt !== '' && parseInt(cnt) > 0){
+ return icon + ' ' + cnt + '';
+ }
+ return icon;
+ },
+ cellClick:function(e, cell){
+ var objid = fnc_checkNull(cell.getData().OBJID);
+ fn_openAddEstimatePdf(objid);
+ }
+ },
// 12. 아마란스 결재상태 (hidden)
{title:'AMARANTH_STATUS', field:'AMARANTH_STATUS', visible: false},
// 13. 결재상태 (아마란스 전자결재)
@@ -556,7 +571,7 @@ function fn_search(){
console.log("품목 검색 조건 설정됨:", partObjId);
}
- _tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/contractMgmt/contractGridList.do", columns, true);
+ _tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/contractMgmt/estimateGridList.do", columns, true);
}
function _fnc_datepick(){
@@ -635,6 +650,14 @@ function fn_delete(){
}
+// 추가견적 PDF 첨부 팝업
+function fn_openAddEstimatePdf(objId){
+ var popup_width = 700;
+ var popup_height = 400;
+ var url = '/contractMgmt/addEstimatePdfPopup.do?targetObjId=' + objId;
+ fn_centerPopup(popup_width, popup_height, url);
+}
+
function fn_FileRegist(objId, docType, docTypeName){
var popup_width = 800;
var popup_height = 300;
diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
index b5474d7..166724f 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp
@@ -1662,14 +1662,14 @@ function fn_generatePdf() {
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
heightLeft -= pageHeight;
- // 페이지가 넘어가면 추가 페이지 생성
- while (heightLeft >= 0) {
+ // 페이지가 넘어가면 추가 페이지 생성 (1mm 이상 넘칠 때만)
+ while (heightLeft > 1) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
heightLeft -= pageHeight;
}
-
+
// 파일명 생성
var estimateNo = $("#estimate_no").val() || "견적서";
var fileName = estimateNo + '.pdf';
@@ -1782,13 +1782,13 @@ function fn_generateAndUploadPdf(callback) {
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
heightLeft -= pageHeight;
- while (heightLeft >= 0) {
+ while (heightLeft > 1) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
heightLeft -= pageHeight;
}
-
+
console.log('PDF 생성 완료');
// PDF를 Base64로 변환
diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp
index c3dd8a7..2573c81 100644
--- a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp
+++ b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp
@@ -1158,7 +1158,7 @@ function fn_generateAndUploadPdf(callback) {
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
heightLeft -= pageHeight;
- while (heightLeft >= 0) {
+ while (heightLeft > 1) {
position = heightLeft - imgHeight;
pdf.addPage();
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
diff --git a/WebContent/WEB-INF/view/partMng/partMngList.jsp b/WebContent/WEB-INF/view/partMng/partMngList.jsp
index 321a6c4..d248cc8 100644
--- a/WebContent/WEB-INF/view/partMng/partMngList.jsp
+++ b/WebContent/WEB-INF/view/partMng/partMngList.jsp
@@ -125,7 +125,7 @@ String connector = person.getUserId();
$('.select2').select2();
// 품번/품명 Select2 AJAX 초기화
- initPartSelect2Ajax("#SEARCH_PART_NO", "#SEARCH_PART_NAME", "#SEARCH_PART_OBJID");
+ // initPartSelect2Ajax("#SEARCH_PART_NO", "#SEARCH_PART_NAME", "#SEARCH_PART_OBJID");
//첨부팝업
$(".File").click(function(){
@@ -760,16 +760,16 @@ function fn_deleteErp(){
|
-
+
+
+
|
|
-
+
+
|
|
diff --git a/src/com/pms/mapper/partMng.xml b/src/com/pms/mapper/partMng.xml
index 23dec61..5b3d2b3 100644
--- a/src/com/pms/mapper/partMng.xml
+++ b/src/com/pms/mapper/partMng.xml
@@ -2001,9 +2001,11 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
AND UPPER(T.PART_NO) LIKE UPPER('%${SEARCH_PART_NO}%')
+
AND UPPER(T.PART_NAME) LIKE UPPER('%${SEARCH_PART_NAME}%')
diff --git a/src/com/pms/salesmgmt/controller/ContractMgmtController.java b/src/com/pms/salesmgmt/controller/ContractMgmtController.java
index 2854520..bcfc7e5 100644
--- a/src/com/pms/salesmgmt/controller/ContractMgmtController.java
+++ b/src/com/pms/salesmgmt/controller/ContractMgmtController.java
@@ -2899,6 +2899,14 @@ public class ContractMgmtController {
request.setAttribute("docTypeName", CommonUtils.checkNull(paramMap.get("docTypeName")));
return "/contractMgmt/FileRegistPopup";
}
+
+ /**
+ * 추가견적 PDF 첨부 팝업
+ */
+ @RequestMapping("/contractMgmt/addEstimatePdfPopup.do")
+ public String addEstimatePdfPopup(HttpServletRequest request, @RequestParam Map paramMap){
+ return "/contractMgmt/addEstimatePdfPopup";
+ }
/**
* 영업정보의 품목 목록 조회 (견적서 작성 시 사용)
diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
index d680e10..a57a65f 100644
--- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml
+++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml
@@ -739,6 +739,8 @@
AND CANCEL_QTY != ''
AND CANCEL_QTY != '0'
) AS CANCEL_QTY_SUM
+ -- 추가견적 PDF 첨부 건수
+ ,(SELECT COUNT(1) FROM ATTACH_FILE_INFO WHERE TARGET_OBJID = T.OBJID AND DOC_TYPE = 'estimate02' AND UPPER(STATUS) = 'ACTIVE') AS ADD_EST_CNT
FROM
CONTRACT_MGMT AS T
LEFT OUTER JOIN
@@ -1014,9 +1016,134 @@
AND COALESCE(IS_DIRECT_ORDER, 'N') != 'Y'
- ORDER BY REGDATE DESC
+ ORDER BY REGDATE DESC
+
+
+
@@ -3302,13 +3422,13 @@ SELECT
, #{qty }
, #{warranty }
- , #{product_price }::NUMERIC
+ , #{product_price }::NUMERIC
- , #{other_price }::NUMERIC
+ , #{other_price }::NUMERIC
- , #{total_price }::NUMERIC
+ , #{total_price }::NUMERIC
, #{contract_user_id }
, #{contract_date }
@@ -3317,20 +3437,20 @@ SELECT
, #{contract_office_no}
, #{contract_fax_no }
- ,#{est_release_date}
+ ,#{est_release_date}
, NOW()
, #{userId}
,(SELECT TO_CHAR(NOW(),'yy')::VARCHAR ||'E-'||LPAD((SELECT NEXTVAL('estimate_mgmt_seq'))::VARCHAR ,4,'0'))
- ,#{contract_product_price}::NUMERIC
+ ,#{contract_product_price}::NUMERIC
,#{sale}
,#{final_total_price}::NUMERIC
,#{contract_type}
,#{note}
- ,#{cus_request_date}
+ ,#{cus_request_date}
,#{delivery_place}
,#{product_code}
diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java
index 72288d3..6763b25 100644
--- a/src/com/pms/salesmgmt/service/ContractMgmtService.java
+++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java
@@ -25,6 +25,8 @@ import org.apache.ibatis.session.SqlSession;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
+import org.apache.pdfbox.multipdf.PDFMergerUtility;
+import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@@ -3406,6 +3408,42 @@ private String encodeImageToBase64(String imagePath) {
* @param estimateTemplate
* @return
*/
+ /**
+ * 견적서 PDF + 추가견적(estimate02) PDF 파일들을 하나로 병합
+ */
+ private File mergePdfWithAddEstimate(File estimatePdf, List