From 2467b34bd0db5f4ff27b1f577b75936025da6e53 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 27 Mar 2026 14:15:47 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EC=A0=9C=ED=92=88=EA=B5=AC=EB=B6=84=20cont?= =?UTF-8?q?ract=5Fmgmt=20->=20contract=5Fitem=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/contractMgmt/orderMgmtList.jsp | 2 +- .../contractMgmt/orderRegistFormPopup.jsp | 40 +++++++++++++------ src/com/pms/mapper/project.xml | 28 ++++++++----- .../SalesNcollectMgmtController.java | 9 ++++- .../service/ContractMgmtService.java | 32 +++++++++------ 5 files changed, 74 insertions(+), 37 deletions(-) diff --git a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp index cf4522e..97903c1 100644 --- a/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/orderMgmtList.jsp @@ -827,7 +827,7 @@ function fn_openOrderConfirmPopup(contractObjId, currentStatus){ fn_search(); }); } else { - Swal.fire("수주확정 저장 중 오류가 발생했습니다."); + Swal.fire("수주확정 저장 중 오류가 발생했습니다.\n" + (data.message || "")); } }, error: function(){ diff --git a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp index 5065589..419701b 100644 --- a/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/orderRegistFormPopup.jsp @@ -169,7 +169,7 @@ // 품목이 없으면 메시지 표시 if($("#itemListBody tr.item-row").length == 0) { - $("#itemListBody").html('품목 추가 버튼을 클릭하여 품목을 등록하세요.'); + $("#itemListBody").html('품목 추가 버튼을 클릭하여 품목을 등록하세요.'); } } } @@ -254,6 +254,12 @@ var html = ''; html += '' + (i + 1) + ''; + // 제품구분 드롭다운 + html += ''; + html += ''; + html += ''; html += ''; html += ''; html += ''; @@ -281,6 +287,11 @@ $("#itemListBody").append(html); + // 제품구분 드롭다운 초기화 (견적요청에서 저장된 값으로 세팅) + var savedProduct = item.PRODUCT || ''; + fnc_getCodeListAppend("0000001", "PRODUCT_" + itemId, savedProduct); + $("#PRODUCT_" + itemId).select2({ width: '100%' }); + // 콤마 추가 (수량은 정수, 금액은 소수점 2자리) $("#" + itemId + " .item-quantity").val(addCommaInt(removeComma($("#" + itemId + " .item-quantity").val()))); $("#" + itemId + " .item-unit-price").val(addComma($("#" + itemId + " .item-unit-price").val())); @@ -351,6 +362,7 @@ var $row = $(this); items.push({ contractItemObjId: $row.find(".item-contract-item-objid").val(), // CONTRACT_ITEM의 OBJID + product: $row.find(".item-product").val(), // 제품구분 partObjId: $row.find(".item-part-objid").val(), partNo: $row.find(".item-part-no").val(), partName: $row.find(".item-part-name").val(), @@ -490,25 +502,27 @@
- - - - - - - - - - + + + + + + + + + + + + - + @@ -518,7 +532,7 @@ - diff --git a/src/com/pms/mapper/project.xml b/src/com/pms/mapper/project.xml index bac22cc..8160011 100644 --- a/src/com/pms/mapper/project.xml +++ b/src/com/pms/mapper/project.xml @@ -7420,7 +7420,7 @@ SELECT ,CONTRACT_OBJID ,CATEGORY_CD ,CUSTOMER_OBJID - ,PRODUCT + ,PRODUCT ,CUSTOMER_PROJECT_NAME ,STATUS_CD ,DUE_DATE @@ -7479,7 +7479,7 @@ SELECT ,#{objId} ,CATEGORY_CD ,CUSTOMER_OBJID - ,PRODUCT + ,COALESCE(#{product}, PRODUCT) ,CUSTOMER_PROJECT_NAME ,STATUS_CD ,DUE_DATE @@ -7529,8 +7529,8 @@ SELECT WHEN '판매' THEN 'S' ELSE 'T' END || '-' || - -- 제품구분 코드 (PRODUCT를 약어로 매핑) - CASE CODE_NAME(PRODUCT) + -- 제품구분 코드 (품목별 PRODUCT를 약어로 매핑, 없으면 마스터 PRODUCT) + CASE CODE_NAME(COALESCE(#{product}, PRODUCT)) WHEN 'Machine' THEN 'MC' WHEN 'A/S' THEN 'AS' WHEN 'D/S' THEN 'DS' @@ -7539,7 +7539,7 @@ SELECT WHEN 'A/C' THEN 'AC' WHEN 'W/M' THEN 'WM' WHEN '기타' THEN '기타' - ELSE REPLACE(CODE_NAME(PRODUCT), '/', '') + ELSE REPLACE(CODE_NAME(COALESCE(#{product}, PRODUCT)), '/', '') END || '-' || -- 날짜 (YYMMDD) TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-' || @@ -7559,7 +7559,7 @@ SELECT WHEN '판매' THEN 'S' ELSE 'T' END || '-' || - CASE CODE_NAME(PRODUCT) + CASE CODE_NAME(COALESCE(#{product}, PRODUCT)) WHEN 'Machine' THEN 'MC' WHEN 'A/S' THEN 'AS' WHEN 'D/S' THEN 'DS' @@ -7568,7 +7568,7 @@ SELECT WHEN 'A/C' THEN 'AC' WHEN 'W/M' THEN 'WM' WHEN '기타' THEN '기타' - ELSE REPLACE(CODE_NAME(PRODUCT), '/', '') + ELSE REPLACE(CODE_NAME(COALESCE(#{product}, PRODUCT)), '/', '') END || '-' || TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-%' ), @@ -7766,11 +7766,21 @@ SELECT MANUFACTURE_PLANT = #{manufacture_plant}, - QUANTITY = #{quantity} + QUANTITY = #{quantity}, + + + PRODUCT = #{product} WHERE CONTRACT_OBJID = #{objId} - AND PART_OBJID = #{part_objid} + + + AND CONTRACT_ITEM_OBJID = #{contract_item_objid} + + + AND PART_OBJID = #{part_objid} + + diff --git a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java index 93520cd..9679070 100644 --- a/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java +++ b/src/com/pms/salesmgmt/controller/SalesNcollectMgmtController.java @@ -302,8 +302,15 @@ public class SalesNcollectMgmtController { contractParam.put("objId", contractObjId); Map contractInfo = commonService.selectOne("contractMgmt.getContractInfo", request, contractParam); - // PROJECT_MGMT에서 품번/품명/S/N/요청납기/고객요청사항/반납사유 조회 + // PROJECT_MGMT에서 품번/품명/S/N/요청납기/고객요청사항/반납사유/제품구분 조회 if(projectInfo != null) { + // 제품구분 (프로젝트에 저장된 품목별 제품구분 우선) + Object projProduct = projectInfo.get("product") != null ? projectInfo.get("product") : projectInfo.get("PRODUCT"); + Object projProductName = projectInfo.get("product_name") != null ? projectInfo.get("product_name") : projectInfo.get("PRODUCT_NAME"); + if(projProduct != null && !"".equals(projProduct.toString())) { + contractInfo.put("PRODUCT", projProduct); + contractInfo.put("PRODUCT_NAME", projProductName); + } // 품번 if(contractInfo.get("PART_NO") == null || "".equals(contractInfo.get("PART_NO"))) { contractInfo.put("PART_NO", projectInfo.get("part_no") != null ? projectInfo.get("part_no") : projectInfo.get("PART_NO")); diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java index 62a4391..3b44642 100644 --- a/src/com/pms/salesmgmt/service/ContractMgmtService.java +++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java @@ -2893,6 +2893,7 @@ private String encodeImageToBase64(String imagePath) { String orderTotalAmount = item.get("orderTotalAmount") != null ? item.get("orderTotalAmount").toString().replace(",", "") : "0"; itemMap.put("contractItemObjId", item.get("contractItemObjId") != null ? item.get("contractItemObjId").toString() : ""); + itemMap.put("product", item.get("product") != null ? item.get("product").toString() : ""); itemMap.put("orderQuantity", item.get("orderQuantity") != null ? item.get("orderQuantity").toString() : ""); itemMap.put("orderUnitPrice", item.get("orderUnitPrice") != null ? item.get("orderUnitPrice").toString() : ""); itemMap.put("orderSupplyPrice", orderSupplyPrice); @@ -2928,18 +2929,16 @@ private String encodeImageToBase64(String imagePath) { // 프로젝트가 이미 존재하는 경우에는 수량/납기 등 업데이트 반영 resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap); if(resultList != null) { - // 제품구분 확인 - Map contractInfo = (Map) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap); - contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo); - String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : ""; - boolean isMachine = "0000928".equals(product_cd); - paramMap.put("contractObjId", contract_objid); List contractItemsRaw2 = sqlSession.selectList("contractMgmt.getContractItems", paramMap); List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw2); if(contractItems != null && !contractItems.isEmpty()) { for(Map item : contractItems) { + // 품목별 제품구분 판단 + String itemProductCd = CommonUtils.checkNull(item.get("PRODUCT")); + boolean isMachine = "0000928".equals(itemProductCd); + Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"); int itemQuantity = 1; try { @@ -2949,6 +2948,7 @@ private String encodeImageToBase64(String imagePath) { } Map updateParam = new HashMap(); updateParam.putAll(paramMap); + updateParam.put("contract_item_objid", String.valueOf(item.get("OBJID"))); updateParam.put("part_objid", item.get("PART_OBJID")); if(isMachine) { updateParam.remove("quantity"); @@ -2956,6 +2956,7 @@ private String encodeImageToBase64(String imagePath) { updateParam.put("quantity", String.valueOf(itemQuantity)); } updateParam.put("due_date", item.get("DUE_DATE")); + updateParam.put("product", itemProductCd); sqlSession.update("project.ModifyProjectByContract", updateParam); } } else { @@ -3005,13 +3006,7 @@ private String encodeImageToBase64(String imagePath) { Map contractInfo = (Map) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap); contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo); - String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : ""; String category_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("CATEGORY_CD")) : ""; - boolean isMachine = "0000928".equals(product_cd); - - if(isMachine) { - System.out.println("[수주확정] 제품구분: Machine(0000928) - 품목별 수량만큼 프로젝트 생성"); - } // 품목별로 프로젝트 생성 또는 업데이트 paramMap.put("contractObjId", objId); @@ -3019,9 +3014,13 @@ private String encodeImageToBase64(String imagePath) { List> contractItems = CommonUtils.toUpperCaseMapKey(contractItemsRaw); if(contractItems != null && !contractItems.isEmpty()) { - System.out.println("[수주확정] 품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : "")); + System.out.println("[수주확정] 품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작"); for(Map item : contractItems) { + // 품목별 제품구분 판단 + String itemProductCd = CommonUtils.checkNull(item.get("PRODUCT")); + boolean isMachine = "0000928".equals(itemProductCd); + // 수량 가져오기 (소수점 형태 "2.00"도 처리) Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"); int itemQuantity = 1; @@ -3034,6 +3033,10 @@ private String encodeImageToBase64(String imagePath) { // Machine인 경우 수량만큼 반복, 아니면 1번만 실행 int loopCount = (isMachine && !hasProject) ? itemQuantity : 1; + if(isMachine) { + System.out.println("[수주확정] 제품구분: Machine(0000928) - 품번: " + item.get("PART_NO") + ", 수량 " + itemQuantity + "만큼 프로젝트 생성"); + } + for(int q = 0; q < loopCount; q++) { if(!hasProject) { // 프로젝트가 없으면 모든 품목에 대해 생성 @@ -3047,6 +3050,8 @@ private String encodeImageToBase64(String imagePath) { projectParam.put("part_objid", item.get("PART_OBJID")); projectParam.put("part_no", item.get("PART_NO")); projectParam.put("part_name", item.get("PART_NAME")); + // 품목별 제품구분 전달 (PROJECT_NO 생성 및 PRODUCT 컬럼에 사용) + projectParam.put("product", itemProductCd); // Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량 projectParam.put("quantity", isMachine ? "1" : String.valueOf(itemQuantity)); projectParam.put("due_date", item.get("DUE_DATE")); @@ -3070,6 +3075,7 @@ private String encodeImageToBase64(String imagePath) { // 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만) Map updateParam = new HashMap(); updateParam.putAll(paramMap); + updateParam.put("contract_item_objid", String.valueOf(item.get("OBJID"))); updateParam.put("part_objid", item.get("PART_OBJID")); // Machine인 경우 수량은 변경하지 않음 (프로젝트별 1로 유지) if(isMachine) { -- 2.49.1 From 3815f4eecdb72ecedb9a8ef98389da5879815d9c Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 27 Mar 2026 14:54:50 +0900 Subject: [PATCH 2/3] =?UTF-8?q?=EC=98=81=EC=97=85=20=ED=8C=9D=EC=97=85?= =?UTF-8?q?=EC=B0=BD=20=EC=BB=AC=EB=9F=BC=20=EB=84=88=EB=B9=84=20=EB=93=B1?= =?UTF-8?q?=20=EC=A1=B0=EC=A0=88,=20=EC=A0=9C=ED=92=88=EA=B5=AC=EB=B6=84?= =?UTF-8?q?=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../estimateAndOrderRegistFormPopup.jsp | 60 +++++++++---------- .../contractMgmt/estimateRegistFormPopup.jsp | 22 +++---- src/com/pms/salesmgmt/mapper/contractMgmt.xml | 3 + .../salesmgmt/mapper/salesNcollectMgmt.xml | 4 +- 4 files changed, 46 insertions(+), 43 deletions(-) diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp index 2d985cf..bf9a41c 100644 --- a/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/estimateAndOrderRegistFormPopup.jsp @@ -41,11 +41,16 @@ section.business_popup_min_width { padding: 0 !important; margin: 0 auto !important; - width: calc(100% - 16px) !important; + max-width: none !important; + width: calc(100% - 8px) !important; + border: none !important; + box-shadow: none !important; } #EntirePopupFormWrap { width: 100% !important; margin: 5px 0 0 0 !important; + padding: 0 5px 10px 5px !important; + box-sizing: border-box; } /* ===== 품목 그리드 컬럼 리사이즈 ===== */ @@ -483,52 +488,52 @@ // S/N html += ''; // 요청납기 html += ''; // 고객요청사항 html += ''; // 반납사유 html += ''; // 수주수량 (Machine이고 프로젝트가 있으면 readonly) html += ''; // 수주단가 html += ''; // 수주공급가액 (자동계산 + 수정가능) html += ''; // 수주부가세 (자동계산 + 수정가능) html += ''; // 수주총액 (자동계산 + 수정가능) html += ''; // 삭제 버튼 @@ -718,46 +723,39 @@ var returnReason = "<%= CommonUtils.checkNull(item.get("RETURN_REASON")) %>"; var savedProduct = "<%= CommonUtils.checkNull(item.get("PRODUCT")) %>"; - // 제품구분 드롭다운 html += ''; - - html += ''; html += ''; html += ''; html += ''; // 수주 정보 (Machine이고 프로젝트가 있으면 수량 readonly) html += ''; html += ''; html += ''; html += ''; html += ''; html += '
No제품구분 * 품번 품명 S/N 수주수량 *수주단가 수주단가 수주공급가액 수주부가세 수주총액
+ 견적요청에 등록된 품목이 자동으로 표시됩니다.
'; - html += ''; + html += ''; html += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; 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 += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; if(isMachine && hasProject) { - html += ''; + html += ''; } else { - html += ''; + html += ''; } html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; - html += ''; + html += ''; html += ''; @@ -1676,13 +1674,13 @@ - + - + - + diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateRegistFormPopup.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateRegistFormPopup.jsp index d2d2a86..2b181a0 100644 --- a/WebContent/WEB-INF/view/contractMgmt/estimateRegistFormPopup.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/estimateRegistFormPopup.jsp @@ -696,13 +696,13 @@ html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += '
'; - html += ''; + html += ''; html += ''; html += ''; html += ''; - html += ''; + html += ''; html += ''; html += ''; @@ -1499,13 +1499,13 @@ html += ''; html += ''; - html += ''; + html += ''; html += ''; html += ''; html += ''; - html += ''; + html += ''; html += ''; html += ''; @@ -2244,15 +2244,15 @@
- - + + - - - + + + - - + + diff --git a/src/com/pms/salesmgmt/mapper/contractMgmt.xml b/src/com/pms/salesmgmt/mapper/contractMgmt.xml index 1ff5dc1..15de0b8 100644 --- a/src/com/pms/salesmgmt/mapper/contractMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/contractMgmt.xml @@ -5288,6 +5288,9 @@ WHERE ORDER_SUPPLY_PRICE = #{orderSupplyPrice}, ORDER_VAT = #{orderVat}, ORDER_TOTAL_AMOUNT = #{orderTotalAmount} + + ,PRODUCT = #{product} + WHERE OBJID = #{contractItemObjId} diff --git a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml index e9b12f2..f9138d2 100644 --- a/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml +++ b/src/com/pms/salesmgmt/mapper/salesNcollectMgmt.xml @@ -1787,8 +1787,10 @@ ORDER BY T.REGDATE DESC, T.PROJECT_NO DESC '; itemsHtml += ''; itemsHtml += ''; - itemsHtml += ''; + itemsHtml += ''; itemsHtml += ''; // 원화환산 공급가액 행 추가 (숨김) @@ -864,7 +887,7 @@ function fn_loadData() { itemsHtml += ''; itemsHtml += ''; itemsHtml += ''; - itemsHtml += ''; + itemsHtml += ''; itemsHtml += ''; // 원화환산 공급가액 행 추가 (숨김) @@ -1082,7 +1105,7 @@ function fn_loadTemplateData(templateObjId){ itemsHtml += ''; itemsHtml += ''; itemsHtml += ''; - itemsHtml += ''; + itemsHtml += ''; itemsHtml += ''; // 원화환산 공급가액 행 추가 (숨김) @@ -1422,7 +1445,7 @@ function fn_save() { - + @@ -1458,7 +1481,7 @@ function fn_save() { - + @@ -1521,15 +1544,17 @@ function fn_replaceFormElementsWithText() { $('.estimate-container').find('input[type="text"], textarea, select').each(function(idx) { var $el = $(this); var displayText = ''; - + if ($el.is('select')) { displayText = $el.find('option:selected').text() || ''; } else { displayText = $el.val() || ''; } - + var computedStyle = window.getComputedStyle(this); - var elWidth = $el.outerWidth(); + // 단가/금액 컬럼이 좁아 잘릴 수 있으므로 부모 td의 내부 너비 사용 + var parentTd = $el.closest('td'); + var elWidth = parentTd.length > 0 ? parentTd.innerWidth() : $el.outerWidth(); var span = $(''); span.text(displayText); span.css({ @@ -1604,29 +1629,31 @@ function fn_generatePdf() { } }); - // 버튼 영역 임시 숨김 + // 버튼 영역 임시 숨김 (삭제 버튼 셀 전체 숨김으로 PDF에 노출 방지) $('.btn-area').hide(); - + $('.btn-delete-row').hide(); + $('.btn-delete-total-row').hide(); + // 폼 요소를 텍스트로 변환 (html2canvas 글자 잘림 방지) var formBackup = fn_replaceFormElementsWithText(); - + // PDF 생성을 위해 스타일 조정 var container = $('.estimate-container'); var originalBg = container.css('background'); var originalShadow = container.css('box-shadow'); var originalPadding = container.css('padding'); - + // 깔끔한 PDF를 위해 배경, 그림자, 패딩 제거 container.css({ 'background': 'white', 'box-shadow': 'none', 'padding': '10mm' }); - + // body 배경색도 흰색으로 var originalBodyBg = $('body').css('background-color'); $('body').css('background-color', 'white'); - + // 견적서 컨테이너 캡처 html2canvas(document.querySelector('.estimate-container'), { scale: 2, // 적절한 해상도 (파일 크기 최적화) @@ -1645,6 +1672,8 @@ function fn_generatePdf() { fn_restoreFormElements(formBackup); // 버튼 영역 다시 표시 $('.btn-area').show(); + $('.btn-delete-row').show(); + $('.btn-delete-total-row').show(); try { // Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음) @@ -1696,6 +1725,8 @@ function fn_generatePdf() { }).catch(function(error) { fn_restoreFormElements(formBackup); $('.btn-area').show(); + $('.btn-delete-row').show(); + $('.btn-delete-total-row').show(); Swal.close(); console.error('Canvas 캡처 오류:', error); Swal.fire({ @@ -1709,7 +1740,7 @@ function fn_generatePdf() { // PDF를 Base64로 생성하여 서버로 전송하는 함수 function fn_generateAndUploadPdf(callback) { console.log('fn_generateAndUploadPdf 호출됨'); - + // 라이브러리 로드 확인 if(typeof html2canvas === 'undefined' || typeof jsPDF === 'undefined') { console.error('필요한 라이브러리가 로드되지 않았습니다.'); @@ -1718,30 +1749,32 @@ function fn_generateAndUploadPdf(callback) { } return; } - - // 버튼 영역 임시 숨김 + + // 버튼 영역 임시 숨김 (삭제 버튼 셀 전체 숨김으로 PDF에 노출 방지) $('.btn-area').hide(); - + $('.btn-delete-row').hide(); + $('.btn-delete-total-row').hide(); + // 폼 요소를 텍스트로 변환 (html2canvas 글자 잘림 방지) var formBackup = fn_replaceFormElementsWithText(); - + // PDF 생성을 위해 스타일 조정 var container = $('.estimate-container'); var originalBg = container.css('background'); var originalShadow = container.css('box-shadow'); var originalPadding = container.css('padding'); - + // 깔끔한 PDF를 위해 배경, 그림자, 패딩 제거 container.css({ 'background': 'white', 'box-shadow': 'none', 'padding': '10mm' }); - + // body 배경색도 흰색으로 var originalBodyBg = $('body').css('background-color'); $('body').css('background-color', 'white'); - + // 견적서 컨테이너 캡처 html2canvas(document.querySelector('.estimate-container'), { scale: 2, // 적절한 해상도 (파일 크기 최적화) @@ -1750,7 +1783,7 @@ function fn_generateAndUploadPdf(callback) { backgroundColor: '#ffffff' }).then(function(canvas) { console.log('Canvas 캡처 완료'); - + // 스타일 복원 container.css({ 'background': originalBg, @@ -1758,12 +1791,14 @@ function fn_generateAndUploadPdf(callback) { 'padding': originalPadding }); $('body').css('background-color', originalBodyBg); - + // 폼 요소 복원 fn_restoreFormElements(formBackup); - + // 버튼 영역 다시 표시 $('.btn-area').show(); + $('.btn-delete-row').show(); + $('.btn-delete-total-row').show(); try { // Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음) @@ -1809,6 +1844,8 @@ function fn_generateAndUploadPdf(callback) { }).catch(function(error) { fn_restoreFormElements(formBackup); $('.btn-area').show(); + $('.btn-delete-row').show(); + $('.btn-delete-total-row').show(); console.error('Canvas 캡처 오류:', error); if(callback && typeof callback === 'function') { callback(null); diff --git a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp index 2573c81..7dc1cdb 100644 --- a/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp +++ b/WebContent/WEB-INF/view/contractMgmt/estimateTemplate2.jsp @@ -29,6 +29,9 @@ boolean isApproved = "결재완료".equals(apprStatus) || "결재중".equals(app .no-print { display: none !important; } + .delete-btn-cell button { + display: none !important; + } } body { @@ -530,10 +533,16 @@ function fn_calculateSubtotal(tbody) { tbody.find(".subtotal-amount").val(addComma(total)); } -// 금액 포맷 (소수점 2자리 + 천단위 콤마) +// 금액 포맷 (항상 소수점 2자리, 천단위 콤마) function addComma(num) { if(num === '' || num === null || num === undefined) return ''; - return Number(num).toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}); + // DB에 콤마가 포함된 값이 저장된 경우를 위해 콤마 제거 후 파싱 + var n = Number(String(num).replace(/,/g, '')); + if(isNaN(n)) return ''; + return n.toLocaleString(undefined, { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); } // 카테고리 추가 @@ -558,7 +567,7 @@ function fn_addCategory() { '' + '' + '' + - '' + '' + @@ -622,7 +631,7 @@ function fn_createCategoryFromData(category) { '' + '' + '' + - '' + '' + @@ -1069,9 +1078,10 @@ function fn_generateAndUploadPdf(callback) { return; } - // 버튼 영역 임시 숨김 + // 버튼 영역 임시 숨김 (삭제 버튼 셀 전체 숨김으로 PDF에 노출 방지) $('.estimate-btn-area').hide(); - + $('.btn-delete-category').hide(); + // textarea를 div로 변환 (줄바꿈 보존) var textareaBackup = []; $('textarea').each(function(index) { @@ -1140,7 +1150,8 @@ function fn_generateAndUploadPdf(callback) { // 버튼 영역 다시 표시 $('.estimate-btn-area').show(); - + $('.btn-delete-category').show(); + try { // Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음) var imgData = canvas.toDataURL('image/jpeg', 0.85); // 85% 품질 @@ -1189,6 +1200,7 @@ function fn_generateAndUploadPdf(callback) { }); $('.estimate-btn-area').show(); + $('.btn-delete-category').show(); console.error('Canvas 캡처 오류:', error); if(callback && typeof callback === 'function') { callback(null); @@ -1302,7 +1314,7 @@ function fn_generateAndUploadPdf(callback) { - @@ -1366,7 +1378,7 @@ Z: SRG25 P급, C0 - @@ -1407,7 +1419,7 @@ Z: SRG25 P급, C0 - @@ -1451,7 +1463,7 @@ SIEMENS CONTROL PANEL 828D - @@ -1521,7 +1533,7 @@ LUBRICATION - @@ -1570,7 +1582,7 @@ NV4Blue(Renishaw) - @@ -1615,7 +1627,7 @@ NV4Blue(Renishaw) - -- 2.49.1
0
0
0
규 격
SPECIFICATION
수량
Q'TY
단위
UNIT
단 가
UNIT
PRICE
단 가
UNIT PRICE
금 액
AMOUNT
비고
0
' + newCategoryNo + '' + newCategoryName + '' + + '' + '' + '
' + categoryNo + '' + categoryName + '' + + '' + '' + '
1 기구 +
2 초음파 스핀들 모듈 +
3 전장 +
4 UTILITY +
5 Option +
6 Set up +
7 포장/물류 +