From d6a75db6572026978f95fd25f93fd001afb2db3a Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 19 Jan 2026 12:06:41 +0900 Subject: [PATCH 1/5] =?UTF-8?q?=EA=B2=AC=EC=A0=81=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=84=9C=EA=B4=80=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/salesMng/purchaseListFormPopUp.jsp | 206 +++++++- .../salesMng/quotationRequestFormPopup.jsp | 447 ++++++++++++++++++ .../view/salesMng/quotationRequestList.jsp | 322 +++++++++++++ .../view/salesMng/salesRequestMngRegList.jsp | 272 ++++++++++- src/com/pms/mapper/salesMng.xml | 441 ++++++++++++++++- .../controller/SalesMngController.java | 218 +++++++++ .../salesmgmt/service/SalesMngService.java | 387 +++++++++++++++ 7 files changed, 2263 insertions(+), 30 deletions(-) create mode 100644 WebContent/WEB-INF/view/salesMng/quotationRequestFormPopup.jsp create mode 100644 WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp index 9d954ce..6993b80 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp @@ -75,6 +75,7 @@ body, html {

구매리스트

+
@@ -216,22 +217,15 @@ function fn_loadInitialData(){ var hasMbomHeader = mbomHeaderObjid && mbomHeaderObjid !== "" && mbomHeaderObjid !== "null"; var hasMaster = salesRequestMasterObjid && salesRequestMasterObjid !== "" && salesRequestMasterObjid !== "null"; - // M-BOM에서 생성된 경우만 M-BOM 데이터를 로드 (MBOM_HEADER_OBJID가 있어야 함) + // M-BOM에서만 데이터를 로드 (MBOM_HEADER_OBJID가 있어야 함) if(hasMbomHeader){ logDebug("purchaseListFormPopUp :: M-BOM에서 생성된 구매리스트 - MBOM 로드"); fn_loadFromMBom(function(mbomList){ - if(hasMaster){ - // M-BOM 데이터가 있으면 merge, 없으면 SALES_REQUEST_PART만 로드 - var hasMbomData = mbomList && mbomList.length > 0; - logDebug("purchaseListFormPopUp :: MBOM data count:", mbomList ? mbomList.length : 0); - fn_loadPurchaseList(hasMbomData); // M-BOM 있으면 merge, 없으면 단독 로드 - } + // M-BOM 데이터만 사용 (SALES_REQUEST_PART 조회 안 함) + logDebug("purchaseListFormPopUp :: MBOM data count:", mbomList ? mbomList.length : 0); }); - } else if(hasMaster){ - logDebug("purchaseListFormPopUp :: 수동 작성된 구매리스트 - SALES_REQUEST_PART만 로드"); - fn_loadPurchaseList(false); } else { - logDebug("purchaseListFormPopUp :: no data to load"); + logDebug("purchaseListFormPopUp :: MBOM_HEADER_OBJID 없음 - 데이터 로드 안 함"); } } @@ -1009,6 +1003,196 @@ function fn_save() { } }); } + +// 견적요청서 생성 +function fn_createQuotationRequest() { + // 체크된 행 가져오기 + var checkedRows = []; + $('.rowCheck:checked').each(function() { + var row = $(this).closest('.tabulator-row'); + var rowData = _tabulGrid.getRow(row.attr('data-row')).getData(); + checkedRows.push(rowData); + }); + + // 체크된 항목이 없으면 전체 데이터 사용 + if(checkedRows.length === 0) { + checkedRows = _tabulGrid.getData(); + } + + if(checkedRows.length === 0) { + Swal.fire({ + title: '알림', + text: '견적요청서를 생성할 데이터가 없습니다.', + icon: 'warning' + }); + return; + } + + // 공급업체/가공업체가 입력된 항목 분류 + var supplyItems = []; // 공급업체가 입력된 항목 + var processingItems = []; // 가공업체가 입력된 항목 + + checkedRows.forEach(function(item) { + var vendorPm = item.VENDOR_PM || ''; + var processingVendor = item.PROCESSING_VENDOR || ''; + + if(vendorPm && vendorPm !== '') { + supplyItems.push({ + objid: item.OBJID, + vendorObjid: vendorPm, + partNo: item.PART_NO, + partName: item.PART_NAME + }); + } + + if(processingVendor && processingVendor !== '') { + processingItems.push({ + objid: item.OBJID, + vendorObjid: processingVendor, + partNo: item.PART_NO, + partName: item.PART_NAME + }); + } + }); + + if(supplyItems.length === 0 && processingItems.length === 0) { + Swal.fire({ + title: '알림', + text: '공급업체 또는 가공업체가 입력된 항목이 없습니다.\n업체를 먼저 입력해주세요.', + icon: 'warning' + }); + return; + } + + // 업체별로 그룹화 + var supplyVendorGroups = {}; + var processingVendorGroups = {}; + + supplyItems.forEach(function(item) { + if(!supplyVendorGroups[item.vendorObjid]) { + supplyVendorGroups[item.vendorObjid] = []; + } + supplyVendorGroups[item.vendorObjid].push(item.objid); + }); + + processingItems.forEach(function(item) { + if(!processingVendorGroups[item.vendorObjid]) { + processingVendorGroups[item.vendorObjid] = []; + } + processingVendorGroups[item.vendorObjid].push(item.objid); + }); + + // 생성할 견적요청서 목록 표시 + var supplyCount = Object.keys(supplyVendorGroups).length; + var processingCount = Object.keys(processingVendorGroups).length; + var totalCount = supplyCount + processingCount; + + var confirmMsg = '견적요청서를 생성하시겠습니까?\n\n'; + if(supplyCount > 0) { + confirmMsg += '- 공급업체: ' + supplyCount + '개 업체\n'; + } + if(processingCount > 0) { + confirmMsg += '- 가공업체: ' + processingCount + '개 업체\n'; + } + confirmMsg += '\n총 ' + totalCount + '개의 견적요청서가 생성됩니다.'; + + Swal.fire({ + title: '견적요청서 생성', + text: confirmMsg, + icon: 'question', + showCancelButton: true, + confirmButtonText: '생성', + cancelButtonText: '취소' + }).then((result) => { + if(result.isConfirmed) { + fn_executeCreateQuotationRequest(supplyVendorGroups, processingVendorGroups); + } + }); +} + +// 견적요청서 생성 실행 +function fn_executeCreateQuotationRequest(supplyVendorGroups, processingVendorGroups) { + var createPromises = []; + + // 공급업체별 견적요청서 생성 + for(var vendorObjid in supplyVendorGroups) { + var partObjids = supplyVendorGroups[vendorObjid]; + createPromises.push( + $.ajax({ + type: "POST", + url: "/salesMng/createQuotationRequest.do", + data: { + SALES_REQUEST_MASTER_OBJID: salesRequestMasterObjid, + VENDOR_OBJID: vendorObjid, + VENDOR_TYPE: 'SUPPLY', + PART_OBJIDS: JSON.stringify(partObjids) + }, + dataType: "json" + }) + ); + } + + // 가공업체별 견적요청서 생성 + for(var vendorObjid in processingVendorGroups) { + var partObjids = processingVendorGroups[vendorObjid]; + createPromises.push( + $.ajax({ + type: "POST", + url: "/salesMng/createQuotationRequest.do", + data: { + SALES_REQUEST_MASTER_OBJID: salesRequestMasterObjid, + VENDOR_OBJID: vendorObjid, + VENDOR_TYPE: 'PROCESSING', + PART_OBJIDS: JSON.stringify(partObjids) + }, + dataType: "json" + }) + ); + } + + Promise.all(createPromises).then(function(results) { + var successCount = 0; + var failCount = 0; + var createdNos = []; + + results.forEach(function(result) { + if(result.resultFlag === 'S') { + successCount++; + if(result.QUOTATION_REQUEST_NO) { + createdNos.push(result.QUOTATION_REQUEST_NO); + } + } else { + failCount++; + } + }); + + if(successCount > 0) { + var msg = successCount + '개의 견적요청서가 생성되었습니다.'; + if(createdNos.length > 0) { + msg += '\n\n생성된 번호:\n' + createdNos.join('\n'); + } + + Swal.fire({ + title: '완료', + text: msg, + icon: 'success' + }); + } else { + Swal.fire({ + title: '실패', + text: '견적요청서 생성에 실패했습니다.', + icon: 'error' + }); + } + }).catch(function(error) { + console.error("견적요청서 생성 오류:", error); + Swal.fire({ + title: '오류', + text: '견적요청서 생성 중 오류가 발생했습니다.', + icon: 'error' + }); + }); +} diff --git a/WebContent/WEB-INF/view/salesMng/quotationRequestFormPopup.jsp b/WebContent/WEB-INF/view/salesMng/quotationRequestFormPopup.jsp new file mode 100644 index 0000000..08d95b6 --- /dev/null +++ b/WebContent/WEB-INF/view/salesMng/quotationRequestFormPopup.jsp @@ -0,0 +1,447 @@ +<%@ 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%> + + + + +
+
+
+

+ 견적요청서 + + - ${resultMap.QUOTATION_REQUEST_NO} + + + + 작성중 + + + 발송완료 + + + 견적수신 + + + 완료 + + +

+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + +
+ + ${resultMap.PROJECT_NO} + + + ${resultMap.PURCHASE_TYPE_NAME} + + + ${resultMap.ORDER_TYPE_NAME} + + + ${resultMap.PRODUCT_NAME_TITLE} +
+ + ${resultMap.REQUEST_MNG_NO} + + + ${resultMap.VENDOR_NAME} + + + 공급업체 + + + 가공업체 + + + + + ${resultMap.VENDOR_EMAIL} +
+ + ${resultMap.MAIL_SEND_DATE_TITLE} +
+
+ +
+
+ <%-- 업체유형 설명 주석처리 + + + 공급업체 견적 - 품번/품명/제작수량 기준 + + + 가공업체 견적 - 소재품번/소재재질/규격/발주수량 기준 + + + --%> + + * 단가 입력 후 저장하면 구매리스트에 자동 반영됩니다. + +
+
+
+
+ + + + diff --git a/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp b/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp new file mode 100644 index 0000000..24a0ae8 --- /dev/null +++ b/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp @@ -0,0 +1,322 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ page import="java.util.*" %> +<%@include file="/init.jsp"%> + + +<% +// DB에서 메뉴명 조회 (공통 유틸 사용) +String menuObjId = request.getParameter("menuObjId"); +String menuName = CommonUtils.getMenuName(menuObjId, "견적요청서관리"); +%> + + + + + <%=Constants.SYSTEM_NAME%> + + + + + + + +
+ + + +
+
+
+

+ <%=menuName%> +

+ +
+ + + +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + +
+ +
+
+ + <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %> +
+
+
+ + diff --git a/WebContent/WEB-INF/view/salesMng/salesRequestMngRegList.jsp b/WebContent/WEB-INF/view/salesMng/salesRequestMngRegList.jsp index 1282524..b3aa175 100644 --- a/WebContent/WEB-INF/view/salesMng/salesRequestMngRegList.jsp +++ b/WebContent/WEB-INF/view/salesMng/salesRequestMngRegList.jsp @@ -116,6 +116,11 @@ $(document).ready(function(){ fn_createProposal(); }); + // 견적요청서 생성 + $("#btnQuotationRequest").click(function(){ + fn_openQuotationRequestPopup(); + }); + $("#btnOrderBOMReg").click(function(){ fn_salesRequestTargetBOMListPopUp(); }); @@ -196,21 +201,25 @@ var columns = [ ,{headerHozAlign : 'center', hozAlign : 'center', title : "유/무상", field :"PAID_TYPE_NAME" , widthGrow:0.9 } ,{headerHozAlign : 'center', hozAlign : 'left', title : "품번", field :"PART_NO" , widthGrow:1.4} ,{headerHozAlign : 'center', hozAlign : 'left' , title : "품명", field :"PART_NAME" , widthGrow:1.8 } - ,{headerHozAlign : 'center', hozAlign : 'center', title : "견적요청서", field :"HAS_PURCHASE_REQUEST" , widthGrow:1.1, + ,{headerHozAlign : 'center', hozAlign : 'center', title : "견적요청서", field :"HAS_QUOTATION_REQUEST" , widthGrow:1.1, formatter: function(cell, formatterParams, onRendered){ - // 구매요청서 작성 여부: HAS_PURCHASE_REQUEST가 'Y'이면 구매요청서 작성됨 + // 견적요청서 존재 여부: HAS_QUOTATION_REQUEST가 'Y'이면 견적요청서 있음 var data = cell.getData(); - var hasPurchaseRequest = fnc_checkNull(data.HAS_PURCHASE_REQUEST); - var iconClass = (hasPurchaseRequest == 'Y') ? 'file_icon' : 'file_empty_icon'; - return ''; + var hasQuotationRequest = fnc_checkNull(data.HAS_QUOTATION_REQUEST); + var quotationCount = parseInt(data.QUOTATION_REQUEST_COUNT) || 0; + var iconClass = (hasQuotationRequest == 'Y') ? 'file_icon' : 'file_empty_icon'; + var countText = quotationCount > 0 ? ' (' + quotationCount + ')' : ''; + return '' + countText; }, cellClick : function(e, cell) { var data = cell.getData(); - var hasPurchaseRequest = fnc_checkNull(data.HAS_PURCHASE_REQUEST); + var hasQuotationRequest = fnc_checkNull(data.HAS_QUOTATION_REQUEST); - // 구매요청서가 작성된 경우(파란색 아이콘)만 팝업 열기 - if(hasPurchaseRequest == 'Y') { - fn_openSalesRequestFormPopUp(data.OBJID); + // 견적요청서가 있는 경우 견적요청서관리 페이지로 이동 (해당 요청번호로 필터링) + if(hasQuotationRequest == 'Y') { + // 견적요청서관리 페이지 팝업으로 열기 + var salesRequestObjid = fnc_checkNull(data.OBJID); + window.open("/salesMng/quotationRequestList.do?SALES_REQUEST_MASTER_OBJID=" + salesRequestObjid, "quotationRequestList", "width=1400,height=800,menubar=no,scrollbars=yes,resizable=yes"); } } } @@ -698,6 +707,250 @@ function fn_executeCreateProposal(salesRequestObjid, targetParts) { } }); } + +/** + * 견적요청서 자동 생성 + * - 선택된 구매요청서의 구매리스트에서 공급업체/가공업체가 입력된 항목으로 견적요청서 자동 생성 + */ +function fn_openQuotationRequestPopup() { + var selectedData = _tabulGrid.getSelectedData(); + + if(selectedData.length < 1) { + Swal.fire({ + title: '알림', + text: '견적요청서를 생성할 행을 선택해주세요.', + icon: 'warning' + }); + return false; + } + + if(selectedData.length > 1) { + Swal.fire({ + title: '알림', + text: '한번에 한 건만 선택해주세요.', + icon: 'warning' + }); + return false; + } + + var salesRequestObjid = fnc_checkNull(selectedData[0].OBJID); + var hasPurchaseRequest = fnc_checkNull(selectedData[0].HAS_PURCHASE_REQUEST); + + // 구매리스트가 작성되지 않은 경우 + if(hasPurchaseRequest !== 'Y') { + Swal.fire({ + title: '알림', + text: '먼저 구매리스트를 작성해주세요.\n(견적요청서 아이콘 클릭하여 구매리스트 작성)', + icon: 'warning' + }); + return false; + } + + // 구매리스트에서 견적요청서 생성 대상 조회 + $.ajax({ + url: "/salesMng/getPurchaseListForQuotation.do", + type: "POST", + data: { + SALES_REQUEST_MASTER_OBJID: salesRequestObjid + }, + dataType: "json", + success: function(response) { + if(response.resultFlag === "S" && response.list && response.list.length > 0) { + fn_processQuotationRequestCreation(salesRequestObjid, response.list); + } else { + Swal.fire({ + title: '알림', + text: '견적요청서를 생성할 대상이 없습니다.\n(공급업체 또는 가공업체가 입력된 항목이 필요합니다)', + icon: 'warning' + }); + } + }, + error: function(xhr, status, error) { + console.error("구매리스트 조회 오류:", error); + Swal.fire({ + title: '오류', + text: '구매리스트 조회 중 오류가 발생했습니다.', + icon: 'error' + }); + } + }); +} + +/** + * 견적요청서 생성 처리 + */ +function fn_processQuotationRequestCreation(salesRequestObjid, purchaseList) { + // 공급업체/가공업체별로 그룹화 + var supplyVendorGroups = {}; // 공급업체별 그룹 + var processingVendorGroups = {}; // 가공업체별 그룹 + + purchaseList.forEach(function(item) { + // 대소문자 모두 처리 (서버에서 소문자로 반환될 수 있음) + var vendorPm = fnc_checkNull(item.VENDOR_PM || item.vendor_pm); + var processingVendor = fnc_checkNull(item.PROCESSING_VENDOR || item.processing_vendor); + var objid = fnc_checkNull(item.OBJID || item.objid); + var vendorName = fnc_checkNull(item.VENDOR_NAME || item.vendor_name); + var processingVendorName = fnc_checkNull(item.PROCESSING_VENDOR_NAME || item.processing_vendor_name); + // 견적요청서 생성 가능 여부 플래그 + var canCreateSupply = fnc_checkNull(item.CAN_CREATE_SUPPLY || item.can_create_supply); + var canCreateProcessing = fnc_checkNull(item.CAN_CREATE_PROCESSING || item.can_create_processing); + + // 공급업체 견적요청서 생성 가능한 경우 + if(vendorPm !== '' && canCreateSupply === 'Y') { + if(!supplyVendorGroups[vendorPm]) { + supplyVendorGroups[vendorPm] = { + vendorName: vendorName, + parts: [] + }; + } + supplyVendorGroups[vendorPm].parts.push(objid); + } + + // 가공업체 견적요청서 생성 가능한 경우 + if(processingVendor !== '' && canCreateProcessing === 'Y') { + if(!processingVendorGroups[processingVendor]) { + processingVendorGroups[processingVendor] = { + vendorName: processingVendorName, + parts: [] + }; + } + processingVendorGroups[processingVendor].parts.push(objid); + } + }); + + var supplyCount = Object.keys(supplyVendorGroups).length; + var processingCount = Object.keys(processingVendorGroups).length; + var totalCount = supplyCount + processingCount; + + if(totalCount === 0) { + Swal.fire({ + title: '알림', + text: '견적요청서를 생성할 대상이 없습니다.\n(공급업체 또는 가공업체가 입력된 항목이 필요합니다)', + icon: 'warning' + }); + return; + } + + // 생성 확인 메시지 + var confirmHtml = '
'; + if(supplyCount > 0) { + confirmHtml += '

공급업체: ' + supplyCount + '개 업체

'; + for(var vendorId in supplyVendorGroups) { + confirmHtml += '- ' + supplyVendorGroups[vendorId].vendorName + ' (' + supplyVendorGroups[vendorId].parts.length + '건)
'; + } + } + if(processingCount > 0) { + confirmHtml += '

가공업체: ' + processingCount + '개 업체

'; + for(var vendorId in processingVendorGroups) { + confirmHtml += '- ' + processingVendorGroups[vendorId].vendorName + ' (' + processingVendorGroups[vendorId].parts.length + '건)
'; + } + } + confirmHtml += '
'; + + Swal.fire({ + title: '견적요청서 생성', + html: '

' + totalCount + '개의 견적요청서가 생성됩니다.

' + confirmHtml, + icon: 'question', + showCancelButton: true, + confirmButtonText: '생성', + cancelButtonText: '취소', + width: '500px' + }).then((result) => { + if(result.isConfirmed) { + fn_executeCreateQuotationRequests(salesRequestObjid, supplyVendorGroups, processingVendorGroups); + } + }); +} + +/** + * 견적요청서 생성 실행 + */ +function fn_executeCreateQuotationRequests(salesRequestObjid, supplyVendorGroups, processingVendorGroups) { + var createPromises = []; + + // 공급업체별 견적요청서 생성 + for(var vendorObjid in supplyVendorGroups) { + var partObjids = supplyVendorGroups[vendorObjid].parts; + createPromises.push( + $.ajax({ + type: "POST", + url: "/salesMng/createQuotationRequest.do", + data: { + SALES_REQUEST_MASTER_OBJID: salesRequestObjid, + VENDOR_OBJID: vendorObjid, + VENDOR_TYPE: 'SUPPLY', + PART_OBJIDS: JSON.stringify(partObjids) + }, + dataType: "json" + }) + ); + } + + // 가공업체별 견적요청서 생성 + for(var vendorObjid in processingVendorGroups) { + var partObjids = processingVendorGroups[vendorObjid].parts; + createPromises.push( + $.ajax({ + type: "POST", + url: "/salesMng/createQuotationRequest.do", + data: { + SALES_REQUEST_MASTER_OBJID: salesRequestObjid, + VENDOR_OBJID: vendorObjid, + VENDOR_TYPE: 'PROCESSING', + PART_OBJIDS: JSON.stringify(partObjids) + }, + dataType: "json" + }) + ); + } + + Promise.all(createPromises).then(function(results) { + var successCount = 0; + var failCount = 0; + var createdNos = []; + + results.forEach(function(result) { + if(result.resultFlag === 'S') { + successCount++; + if(result.QUOTATION_REQUEST_NO) { + createdNos.push(result.QUOTATION_REQUEST_NO); + } + } else { + failCount++; + } + }); + + if(successCount > 0) { + var msgHtml = '

' + successCount + '개의 견적요청서가 생성되었습니다.

'; + if(createdNos.length > 0) { + msgHtml += '
생성된 번호:
'; + msgHtml += createdNos.map(function(no) { return '- ' + no; }).join('
'); + msgHtml += '
'; + } + + Swal.fire({ + title: '완료', + html: msgHtml, + icon: 'success' + }).then(() => { + fn_search(); + }); + } else { + Swal.fire({ + title: '실패', + text: '견적요청서 생성에 실패했습니다.', + icon: 'error' + }); + } + }).catch(function(error) { + console.error("견적요청서 생성 오류:", error); + Swal.fire({ + title: '오류', + text: '견적요청서 생성 중 오류가 발생했습니다.', + icon: 'error' + }); + }); +} @@ -716,6 +969,7 @@ function fn_executeCreateProposal(salesRequestObjid, targetParts) {
+
diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index 9143a35..1513172 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -989,11 +989,24 @@ VALUES -- 문서유형 (PURCHASE_REQUEST: 구매요청서, PROPOSAL: 품의서) SRM.DOC_TYPE, - -- 구매요청서 작성 여부 (SALES_REQUEST_PART에 DOC_TYPE이 PURCHASE_REQUEST인 데이터가 있으면 'Y') - (SELECT CASE WHEN COUNT(*) > 0 THEN 'Y' ELSE 'N' END - FROM SALES_REQUEST_PART - WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID - AND (DOC_TYPE = 'PURCHASE_REQUEST' OR DOC_TYPE IS NULL)) AS HAS_PURCHASE_REQUEST, + -- 구매요청서 작성 여부 + -- M-BOM 기반이면 MBOM_HEADER_OBJID가 있으므로 'Y' + -- 수동 작성이면 SALES_REQUEST_PART에 데이터가 있으면 'Y' + CASE + WHEN SRM.MBOM_HEADER_OBJID IS NOT NULL AND SRM.MBOM_HEADER_OBJID::VARCHAR != '' THEN 'Y' + WHEN (SELECT COUNT(*) FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID AND (DOC_TYPE = 'PURCHASE_REQUEST' OR DOC_TYPE IS NULL)) > 0 THEN 'Y' + ELSE 'N' + END AS HAS_PURCHASE_REQUEST, + + -- 견적요청서 존재 여부 (QUOTATION_REQUEST_MASTER에 데이터가 있으면 'Y') + COALESCE((SELECT CASE WHEN COUNT(*) > 0 THEN 'Y' ELSE 'N' END + FROM QUOTATION_REQUEST_MASTER + WHERE SALES_REQUEST_MASTER_OBJID::VARCHAR = SRM.OBJID::VARCHAR), 'N') AS HAS_QUOTATION_REQUEST, + + -- 견적요청서 개수 + COALESCE((SELECT COUNT(*) + FROM QUOTATION_REQUEST_MASTER + WHERE SALES_REQUEST_MASTER_OBJID::VARCHAR = SRM.OBJID::VARCHAR), 0) AS QUOTATION_REQUEST_COUNT, -- M-BOM 관련 컬럼 SRM.MBOM_HEADER_OBJID, @@ -3075,7 +3088,7 @@ UPDATE SET TOTAL_PRICE = #{TOTAL_PRICE} - + + SELECT + MD.OBJID, + MD.MBOM_HEADER_OBJID, + MD.PART_OBJID, + PM.PART_NO, + PM.PART_NAME, + MD.RAW_MATERIAL, + MD.RAW_MATERIAL_PART_NO AS RAW_MATERIAL_NO, + MD.RAW_MATERIAL_SIZE AS SIZE, + MD.PO_QTY, + MD.PRODUCTION_QTY, + MD.UNIT_PRICE, + MD.PROCESSING_UNIT_PRICE, + MD.VENDOR AS VENDOR_PM, + (SELECT CLIENT_NM FROM CLIENT_MNG WHERE OBJID::VARCHAR = MD.VENDOR) AS VENDOR_NAME, + MD.PROCESSING_VENDOR, + (SELECT CLIENT_NM FROM CLIENT_MNG WHERE OBJID::VARCHAR = MD.PROCESSING_VENDOR) AS PROCESSING_VENDOR_NAME + FROM MBOM_DETAIL MD + LEFT JOIN PART_MNG PM ON MD.PART_OBJID::VARCHAR = PM.OBJID::VARCHAR + WHERE MD.OBJID = #{OBJID} + + + + + + + + + + + + + + + + + + + + + INSERT INTO QUOTATION_REQUEST_MASTER ( + OBJID, + QUOTATION_REQUEST_NO, + SALES_REQUEST_MASTER_OBJID, + PROJECT_MGMT_OBJID, + VENDOR_OBJID, + VENDOR_TYPE, + STATUS, + DUE_DATE, + REMARK, + WRITER, + REG_DATE + ) VALUES ( + #{OBJID}::NUMERIC, + #{QUOTATION_REQUEST_NO}, + #{SALES_REQUEST_MASTER_OBJID}::NUMERIC, + #{PROJECT_MGMT_OBJID}::NUMERIC, + #{VENDOR_OBJID}::NUMERIC, + #{VENDOR_TYPE}, + 'create', + #{DUE_DATE}::DATE + NULL, + #{REMARK}, + #{WRITER}, + NOW() + ) + + + + + INSERT INTO QUOTATION_REQUEST_DETAIL ( + OBJID, + QUOTATION_REQUEST_MASTER_OBJID, + SALES_REQUEST_PART_OBJID, + PART_OBJID, + PART_NO, + PART_NAME, + RAW_MATERIAL, + SIZE, + QTY, + UNIT_PRICE, + REMARK, + REG_DATE + ) VALUES ( + #{OBJID}::NUMERIC, + #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC, + #{SALES_REQUEST_PART_OBJID}::NUMERIC, + #{PART_OBJID}::NUMERIC, + #{PART_NO}, + #{PART_NAME}, + #{RAW_MATERIAL}, + #{SIZE}, + COALESCE(#{QTY}::NUMERIC, 0), + COALESCE(#{UNIT_PRICE}::NUMERIC, 0), + #{REMARK}, + NOW() + ) + + + + + UPDATE QUOTATION_REQUEST_DETAIL SET + UNIT_PRICE = #{UNIT_PRICE}::NUMERIC, + TOTAL_PRICE = #{QTY}::NUMERIC * #{UNIT_PRICE}::NUMERIC, + EDIT_DATE = NOW() + WHERE OBJID = #{OBJID}::NUMERIC + + + + + UPDATE QUOTATION_REQUEST_MASTER SET + STATUS = #{STATUS}, + EDIT_DATE = NOW() + + , MAIL_SEND_YN = #{MAIL_SEND_YN} + , MAIL_SEND_DATE = NOW() + + WHERE OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + + + + UPDATE MBOM_DETAIL SET + + UNIT_PRICE = #{UNIT_PRICE}::NUMERIC + + + PROCESSING_UNIT_PRICE = #{UNIT_PRICE}::NUMERIC + + , EDIT_DATE = NOW() + WHERE OBJID = #{SALES_REQUEST_PART_OBJID} + + + + + + + + + + + + DELETE FROM QUOTATION_REQUEST_MASTER WHERE OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + + + + DELETE FROM QUOTATION_REQUEST_DETAIL WHERE QUOTATION_REQUEST_MASTER_OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + \ No newline at end of file diff --git a/src/com/pms/salesmgmt/controller/SalesMngController.java b/src/com/pms/salesmgmt/controller/SalesMngController.java index 9cc9064..0311108 100644 --- a/src/com/pms/salesmgmt/controller/SalesMngController.java +++ b/src/com/pms/salesmgmt/controller/SalesMngController.java @@ -1538,4 +1538,222 @@ public class SalesMngController { return resultMap; } + + // ===================================================== + // 견적요청서 관리 컨트롤러 + // ===================================================== + + /** + * 견적요청서 목록 페이지 + * @param request + * @param paramMap + * @return + */ + @RequestMapping("/salesMng/quotationRequestList.do") + public String quotationRequestList(HttpServletRequest request, @RequestParam Map paramMap){ + String returnUrl = "/salesMng/quotationRequestList"; + Map code_map = new HashMap(); + + try { + // 프로젝트번호 + code_map.put("project_no", commonService.bizMakeOptionList("", (String)paramMap.get("project_no"), "common.getProjectNameList")); + // 업체 목록 + code_map.put("vendor_objid", commonService.bizMakeOptionList("", (String)paramMap.get("vendor_objid"), "common.getsupplyselect")); + // 상태 + String statusOptions = ""; + statusOptions += ""; + statusOptions += ""; + statusOptions += ""; + statusOptions += ""; + statusOptions += ""; + code_map.put("status", statusOptions); + + } catch (Exception e) { + e.printStackTrace(); + } + + request.setAttribute("code_map", code_map); + return returnUrl; + } + + /** + * 견적요청서 목록 조회 (페이징) + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/quotationRequestListPaging.do") + public Map getQuotationRequestListPaging(HttpServletRequest request, @RequestParam Map paramMap){ + commonService.selectListPagingNew("salesMng.getQuotationRequestList", request, paramMap); + return paramMap; + } + + /** + * 견적요청서 상세 팝업 + * @param request + * @param paramMap + * @return + */ + @RequestMapping("/salesMng/quotationRequestFormPopup.do") + public String quotationRequestFormPopup(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + Map code_map = new HashMap(); + + try { + String quotationRequestMasterObjid = CommonUtils.checkNull(paramMap.get("QUOTATION_REQUEST_MASTER_OBJID")); + + if(!"".equals(quotationRequestMasterObjid)){ + // 기존 견적요청서 조회 + resultMap = salesMngService.getQuotationRequestMasterInfo(request, paramMap); + } else { + // 신규 생성 (구매리스트에서 호출 시) + resultMap.put("OBJID", CommonUtils.createObjId()); + resultMap.put("STATUS", "create"); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + request.setAttribute("code_map", code_map); + request.setAttribute("resultMap", resultMap); + return "/salesMng/quotationRequestFormPopup"; + } + + /** + * 견적요청서 상세 목록 조회 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/getQuotationRequestDetailList.do") + public Map getQuotationRequestDetailList(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + List detailList = salesMngService.getQuotationRequestDetailList(request, paramMap); + resultMap.put("list", detailList); + resultMap.put("resultFlag", "S"); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "조회 중 오류가 발생했습니다."); + } + + return resultMap; + } + + /** + * 견적요청서 생성 (구매리스트에서) + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/createQuotationRequest.do") + public Map createQuotationRequest(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = salesMngService.createQuotationRequest(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "견적요청서 생성 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + + /** + * 견적요청서 단가 저장 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/saveQuotationRequestPrice.do") + public Map saveQuotationRequestPrice(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = salesMngService.saveQuotationRequestPrice(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "저장 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + + /** + * 견적요청서 메일 발송 후 상태 업데이트 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/updateQuotationRequestMailSent.do") + public Map updateQuotationRequestMailSent(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = salesMngService.updateQuotationRequestMailSent(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "상태 업데이트 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + + /** + * 견적요청서 삭제 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/deleteQuotationRequest.do") + public Map deleteQuotationRequest(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + resultMap = salesMngService.deleteQuotationRequest(request, paramMap); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + + return resultMap; + } + + /** + * 구매리스트에서 견적요청서 생성 대상 조회 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/salesMng/getPurchaseListForQuotation.do") + public Map getPurchaseListForQuotation(HttpServletRequest request, @RequestParam Map paramMap){ + Map resultMap = new HashMap(); + + try { + List list = salesMngService.getPurchaseListForQuotation(request, paramMap); + resultMap.put("list", list); + resultMap.put("resultFlag", "S"); + } catch (Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "조회 중 오류가 발생했습니다."); + } + + return resultMap; + } } diff --git a/src/com/pms/salesmgmt/service/SalesMngService.java b/src/com/pms/salesmgmt/service/SalesMngService.java index a488fa4..5daba64 100644 --- a/src/com/pms/salesmgmt/service/SalesMngService.java +++ b/src/com/pms/salesmgmt/service/SalesMngService.java @@ -2194,4 +2194,391 @@ public class SalesMngService { return proposalNo; } + + // ===================================================== + // 견적요청서 관리 메서드 + // ===================================================== + + /** + * 견적요청서 목록 조회 + * @param request + * @param paramMap + * @return + */ + public List getQuotationRequestList(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + List resultList = new ArrayList(); + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + resultList = sqlSession.selectList("salesMng.getQuotationRequestList", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) sqlSession.close(); + } + return resultList; + } + + /** + * 견적요청서 마스터 정보 조회 + * @param request + * @param paramMap + * @return + */ + public Map getQuotationRequestMasterInfo(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + resultMap = (Map)sqlSession.selectOne("salesMng.getQuotationRequestMasterInfo", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) sqlSession.close(); + } + return resultMap; + } + + /** + * 견적요청서 상세 목록 조회 + * @param request + * @param paramMap + * @return + */ + public List getQuotationRequestDetailList(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + List resultList = new ArrayList(); + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + resultList = sqlSession.selectList("salesMng.getQuotationRequestDetailList", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) sqlSession.close(); + } + return resultList; + } + + /** + * 견적요청서 생성 (구매리스트에서) + * @param request + * @param paramMap + * @return + */ + public Map createQuotationRequest(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String salesRequestMasterObjid = CommonUtils.checkNull(paramMap.get("SALES_REQUEST_MASTER_OBJID")); + String vendorObjid = CommonUtils.checkNull(paramMap.get("VENDOR_OBJID")); + String vendorType = CommonUtils.checkNull(paramMap.get("VENDOR_TYPE")); // SUPPLY 또는 PROCESSING + String partObjidsJson = CommonUtils.checkNull(paramMap.get("PART_OBJIDS")); // 선택된 품목 OBJID 목록 + + HttpSession session = request.getSession(); + PersonBean personBean = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String userId = personBean.getUserId(); + + // 1. 견적요청서 번호 생성 + String quotationRequestNo = (String)sqlSession.selectOne("salesMng.getNextQuotationRequestNo"); + + // 2. 구매요청서 마스터 정보 조회 (프로젝트 정보 등) + Map masterParam = new HashMap(); + masterParam.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + Map purchaseRequestInfo = (Map)sqlSession.selectOne("salesMng.getSalesRequestMasterInfo", masterParam); + + // 3. 견적요청서 마스터 생성 + String quotationMasterObjid = CommonUtils.createObjId(); + Map quotationMaster = new HashMap(); + quotationMaster.put("OBJID", quotationMasterObjid); + quotationMaster.put("QUOTATION_REQUEST_NO", quotationRequestNo); + quotationMaster.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + // 대소문자 모두 처리 (MyBatis에서 소문자로 반환될 수 있음) + Object projectMgmtObjid = null; + Object dueDate = null; + if(purchaseRequestInfo != null) { + projectMgmtObjid = purchaseRequestInfo.get("PROJECT_MGMT_OBJID") != null ? + purchaseRequestInfo.get("PROJECT_MGMT_OBJID") : purchaseRequestInfo.get("project_mgmt_objid"); + dueDate = purchaseRequestInfo.get("DUE_DATE") != null ? + purchaseRequestInfo.get("DUE_DATE") : purchaseRequestInfo.get("due_date"); + } + quotationMaster.put("PROJECT_MGMT_OBJID", projectMgmtObjid); + quotationMaster.put("VENDOR_OBJID", vendorObjid); + quotationMaster.put("VENDOR_TYPE", vendorType); + quotationMaster.put("DUE_DATE", dueDate); + quotationMaster.put("WRITER", userId); + + sqlSession.insert("salesMng.insertQuotationRequestMaster", quotationMaster); + + // 4. 선택된 품목들로 견적요청서 상세 생성 + List partObjids = new ArrayList(); + if(partObjidsJson != null && !partObjidsJson.isEmpty()) { + org.codehaus.jackson.map.ObjectMapper mapper = new org.codehaus.jackson.map.ObjectMapper(); + partObjids = mapper.readValue(partObjidsJson, List.class); + } + + for(String partObjid : partObjids) { + // 구매리스트 품목 정보 조회 + Map partParam = new HashMap(); + partParam.put("OBJID", partObjid); + Map partInfo = (Map)sqlSession.selectOne("salesMng.getSalesRequestPartInfo", partParam); + + if(partInfo != null) { + Map detailParam = new HashMap(); + detailParam.put("OBJID", CommonUtils.createObjId()); + detailParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + detailParam.put("SALES_REQUEST_PART_OBJID", partObjid); // MBOM_DETAIL.OBJID + detailParam.put("PART_OBJID", partInfo.get("PART_OBJID")); + + // 업체유형에 따라 다른 정보 저장 (단가는 0으로, 견적 수신 후 입력) + if("PROCESSING".equals(vendorType)) { + // 가공업체: 품번, 품명, 제작수량 + detailParam.put("PART_NO", partInfo.get("PART_NO")); + detailParam.put("PART_NAME", partInfo.get("PART_NAME")); + detailParam.put("RAW_MATERIAL", ""); + detailParam.put("SIZE", ""); + detailParam.put("QTY", partInfo.get("PRODUCTION_QTY")); + } else { + // 공급업체: 소재품번, 소재재질, 규격, 발주수량 + detailParam.put("PART_NO", partInfo.get("RAW_MATERIAL_NO")); + detailParam.put("PART_NAME", partInfo.get("RAW_MATERIAL")); + detailParam.put("RAW_MATERIAL", partInfo.get("RAW_MATERIAL")); + detailParam.put("SIZE", partInfo.get("SIZE")); + detailParam.put("QTY", partInfo.get("PO_QTY")); + } + detailParam.put("UNIT_PRICE", 0); // 단가는 견적 수신 후 입력 + detailParam.put("REMARK", ""); + + sqlSession.insert("salesMng.insertQuotationRequestDetail", detailParam); + } + } + + sqlSession.commit(); + + resultMap.put("resultFlag", "S"); + resultMap.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + resultMap.put("QUOTATION_REQUEST_NO", quotationRequestNo); + resultMap.put("message", "견적요청서가 생성되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "견적요청서 생성 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 견적요청서 단가 저장 및 구매리스트 업데이트 + * @param request + * @param paramMap + * @return + */ + public Map saveQuotationRequestPrice(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String quotationMasterObjid = CommonUtils.checkNull(paramMap.get("QUOTATION_REQUEST_MASTER_OBJID")); + String detailListJson = CommonUtils.checkNull(paramMap.get("DETAIL_LIST")); + + // JSON 파싱 + List detailList = new ArrayList(); + if(detailListJson != null && !detailListJson.isEmpty()) { + org.codehaus.jackson.map.ObjectMapper mapper = new org.codehaus.jackson.map.ObjectMapper(); + detailList = mapper.readValue(detailListJson, List.class); + } + + // 견적요청서 마스터 정보 조회 (VENDOR_TYPE 확인) + Map masterParam = new HashMap(); + masterParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + Map masterInfo = (Map)sqlSession.selectOne("salesMng.getQuotationRequestMasterInfo", masterParam); + String vendorType = CommonUtils.checkNull(masterInfo.get("VENDOR_TYPE")); + + // 각 상세 항목 업데이트 + for(Map detail : detailList) { + String detailObjid = CommonUtils.checkNull(detail.get("OBJID")); + String salesRequestPartObjid = CommonUtils.checkNull(detail.get("SALES_REQUEST_PART_OBJID")); + String unitPrice = CommonUtils.checkNull(detail.get("UNIT_PRICE")); + String qty = CommonUtils.checkNull(detail.get("QTY")); + + // 1. 견적요청서 상세 단가 업데이트 + Map updateParam = new HashMap(); + updateParam.put("OBJID", detailObjid); + updateParam.put("UNIT_PRICE", unitPrice); + updateParam.put("QTY", qty); + sqlSession.update("salesMng.updateQuotationRequestDetailPrice", updateParam); + + // 2. 구매리스트 단가 업데이트 + Map purchaseUpdateParam = new HashMap(); + purchaseUpdateParam.put("SALES_REQUEST_PART_OBJID", salesRequestPartObjid); + purchaseUpdateParam.put("UNIT_PRICE", unitPrice); + purchaseUpdateParam.put("VENDOR_TYPE", vendorType); + sqlSession.update("salesMng.updatePurchaseListPriceFromQuotation", purchaseUpdateParam); + } + + // 3. 견적요청서 마스터 상태 업데이트 (received: 견적수신) + Map statusParam = new HashMap(); + statusParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + statusParam.put("STATUS", "received"); + sqlSession.update("salesMng.updateQuotationRequestMasterStatus", statusParam); + + sqlSession.commit(); + + resultMap.put("resultFlag", "S"); + resultMap.put("message", "저장되었습니다. 구매리스트 단가가 업데이트되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "저장 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 견적요청서 메일 발송 후 상태 업데이트 + * @param request + * @param paramMap + * @return + */ + public Map updateQuotationRequestMailSent(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String quotationMasterObjid = CommonUtils.checkNull(paramMap.get("QUOTATION_REQUEST_MASTER_OBJID")); + + Map statusParam = new HashMap(); + statusParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + statusParam.put("STATUS", "sent"); + statusParam.put("MAIL_SEND_YN", "Y"); + sqlSession.update("salesMng.updateQuotationRequestMasterStatus", statusParam); + + sqlSession.commit(); + + resultMap.put("resultFlag", "S"); + resultMap.put("message", "메일 발송이 완료되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "상태 업데이트 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 견적요청서 삭제 + * @param request + * @param paramMap + * @return + */ + public Map deleteQuotationRequest(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String quotationMasterObjid = CommonUtils.checkNull(paramMap.get("QUOTATION_REQUEST_MASTER_OBJID")); + + Map deleteParam = new HashMap(); + deleteParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + + // 상세 먼저 삭제 + sqlSession.delete("salesMng.deleteQuotationRequestDetail", deleteParam); + // 마스터 삭제 + sqlSession.delete("salesMng.deleteQuotationRequestMaster", deleteParam); + + sqlSession.commit(); + + resultMap.put("resultFlag", "S"); + resultMap.put("message", "삭제되었습니다."); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "삭제 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 구매리스트에서 견적요청서 생성 대상 조회 + * - MBOM_DETAIL에서만 조회 (구매리스트관리는 M-BOM 기반으로만 동작) + * @param request + * @param paramMap + * @return + */ + public List getPurchaseListForQuotation(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + List resultList = new ArrayList(); + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + String salesRequestMasterObjid = CommonUtils.checkNull(paramMap.get("SALES_REQUEST_MASTER_OBJID")); + System.out.println("========== getPurchaseListForQuotation =========="); + System.out.println("SALES_REQUEST_MASTER_OBJID: " + salesRequestMasterObjid); + + // SALES_REQUEST_MASTER에서 MBOM_HEADER_OBJID 조회 + Map masterParam = new HashMap(); + masterParam.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + Map masterInfo = (Map)sqlSession.selectOne("salesMng.getSalesRequestMasterInfo", masterParam); + + System.out.println("masterInfo: " + masterInfo); + + if(masterInfo != null) { + // 대소문자 구분 없이 MBOM_HEADER_OBJID 조회 + String mbomHeaderObjid = ""; + if(masterInfo.get("MBOM_HEADER_OBJID") != null) { + mbomHeaderObjid = CommonUtils.checkNull(masterInfo.get("MBOM_HEADER_OBJID")); + } else if(masterInfo.get("mbom_header_objid") != null) { + mbomHeaderObjid = CommonUtils.checkNull(masterInfo.get("mbom_header_objid")); + } + + System.out.println("MBOM_HEADER_OBJID: " + mbomHeaderObjid); + + if(!mbomHeaderObjid.isEmpty()) { + // MBOM_DETAIL에서 조회 + Map queryParam = new HashMap(); + queryParam.put("MBOM_HEADER_OBJID", mbomHeaderObjid); + queryParam.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + resultList = sqlSession.selectList("salesMng.getPurchaseListForQuotationFromMBom", queryParam); + System.out.println("조회 결과 건수: " + resultList.size()); + } else { + System.out.println("MBOM_HEADER_OBJID가 비어있음"); + } + } else { + System.out.println("masterInfo가 null"); + } + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) sqlSession.close(); + } + return resultList; + } } -- 2.49.1 From 8d14504861a94d18f2fa4751095caff9c8e28794 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 19 Jan 2026 14:13:43 +0900 Subject: [PATCH 2/5] =?UTF-8?q?=EA=B2=AC=EC=A0=81=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=84=9C=20=EB=A9=94=EC=9D=BC=20=EB=B0=9C=EC=86=A1=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(=EA=B2=AC=EC=A0=81=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=EC=84=9C=20pdf=20=EB=B3=80=ED=99=98=20=EC=B2=A8?= =?UTF-8?q?=EB=B6=80=20=EA=B8=B0=EB=8A=A5=EC=9D=80=20=EA=B0=9C=EB=B0=9C?= =?UTF-8?q?=EC=A0=84.=20=EC=95=84=EC=A7=81=20=EC=96=91=EC=8B=9D=20?= =?UTF-8?q?=EB=AA=BB=EB=B0=9B=EC=9D=8C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/salesMng/quotationRequestList.jsp | 54 ++- .../salesMng/quotationRequestMailPopup.jsp | 411 ++++++++++++++++++ src/com/pms/mapper/salesMng.xml | 8 + .../controller/SalesMngController.java | 27 ++ .../salesmgmt/service/SalesMngService.java | 120 +++++ 5 files changed, 607 insertions(+), 13 deletions(-) create mode 100644 WebContent/WEB-INF/view/salesMng/quotationRequestMailPopup.jsp diff --git a/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp b/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp index 24a0ae8..f42f22a 100644 --- a/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp +++ b/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp @@ -124,14 +124,10 @@ var columns = [ } }, {headerHozAlign:'center', hozAlign:'center', widthGrow:0.6, title:'수신견적서', field:'ATTACH_FILE_CNT', - formatter: function(cell, formatterParams, onRendered){ - var cnt = cell.getValue() || 0; - var iconClass = cnt > 0 ? 'file_icon' : 'file_empty_icon'; - return ''; - }, + formatter: fnc_subInfoValueFormatter, cellClick: function(e, cell){ var objId = fnc_checkNull(cell.getData().OBJID); - fn_openAttachFile(objId); + fn_openAttachFilePopUp(objId); } } ]; @@ -151,12 +147,44 @@ function fn_formPopUp(objId){ } // 수신견적서 첨부파일 팝업 -function fn_openAttachFile(objId){ +function fn_openAttachFilePopUp(objId) { var popup_width = 800; - var popup_height = 600; - var url = "/common/attachFilePopup.do?TARGET_OBJID=" + objId + "&DOC_TYPE=QUOTATION_RECEIVED&TITLE=수신견적서"; + var popup_height = 300; + var params = "?targetObjId=" + objId + "&docType=QUOTATION_RECEIVED&docTypeName=수신견적서"; + var url = "/common/FileRegistPopup.do" + params; + var popup = window.open(url, "attachFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes"); - window.open(url, "attachFilePopup", "width="+popup_width+",height="+popup_height+",menubar=no,scrollbars=yes,resizable=yes"); + // 팝업 닫힘 감지하여 해당 행의 파일 카운트 업데이트 + fn_watchPopupClose(popup, objId, 'ATTACH_FILE_CNT', 'QUOTATION_RECEIVED'); +} + +// 팝업 닫힘 감지 및 파일 카운트 업데이트 +function fn_watchPopupClose(popup, objId, fieldName, docType) { + var checkPopup = setInterval(function() { + if(popup.closed) { + clearInterval(checkPopup); + fn_updateFileCnt(objId, fieldName, docType); + } + }, 500); +} + +// 파일 카운트 업데이트 +function fn_updateFileCnt(objId, fieldName, docType) { + $.ajax({ + url: "/common/getFileList.do", + type: "POST", + data: { targetObjId: objId, docType: docType }, + dataType: "json", + success: function(data) { + var cnt = data ? data.length : 0; + var rows = _tabulGrid.getRows(); + rows.forEach(function(row) { + if(row.getData().OBJID == objId) { + row.update({ [fieldName]: cnt }); + } + }); + } + }); } // 삭제 @@ -256,9 +284,9 @@ function fn_sendMail(){ // 메일 발송 팝업 function fn_openMailFormPopup(quotationRequestObjId){ - var popup_width = 900; - var popup_height = 750; - var url = "/salesMng/quotationRequestMailFormPopup.do?QUOTATION_REQUEST_MASTER_OBJID=" + quotationRequestObjId; + var popup_width = 850; + var popup_height = 650; + var url = "/salesMng/quotationRequestMailPopup.do?QUOTATION_REQUEST_MASTER_OBJID=" + quotationRequestObjId; window.open(url, "quotationRequestMailForm", "width="+popup_width+",height="+popup_height+",menubar=no,scrollbars=yes,resizable=yes"); } diff --git a/WebContent/WEB-INF/view/salesMng/quotationRequestMailPopup.jsp b/WebContent/WEB-INF/view/salesMng/quotationRequestMailPopup.jsp new file mode 100644 index 0000000..5311ad8 --- /dev/null +++ b/WebContent/WEB-INF/view/salesMng/quotationRequestMailPopup.jsp @@ -0,0 +1,411 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> +<% +PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); +String connector = person.getUserId(); +String quotationRequestMasterObjid = request.getParameter("QUOTATION_REQUEST_MASTER_OBJID"); +%> + + + + + 견적요청서 메일 발송 + + + +
+
견적요청서 메일 발송
+ + +
+
+ 견적번호: + - +
+
+ 업체명: + - +
+
+ 프로젝트: + - +
+
+ +
+ + + +
+ + +
예: email1@example.com, email2@example.com
+
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+
+ + + + diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index 1513172..c7596c0 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -4685,4 +4685,12 @@ ORDER BY V.PATH2 DELETE FROM QUOTATION_REQUEST_DETAIL WHERE QUOTATION_REQUEST_MASTER_OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + + UPDATE QUOTATION_REQUEST_MASTER + SET MAIL_SEND_YN = 'Y', + MAIL_SEND_DATE = NOW() + WHERE OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + \ No newline at end of file diff --git a/src/com/pms/salesmgmt/controller/SalesMngController.java b/src/com/pms/salesmgmt/controller/SalesMngController.java index 0311108..59fee7e 100644 --- a/src/com/pms/salesmgmt/controller/SalesMngController.java +++ b/src/com/pms/salesmgmt/controller/SalesMngController.java @@ -20,6 +20,7 @@ import javax.servlet.http.HttpSession; import org.apache.ibatis.session.SqlSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; @@ -1756,4 +1757,30 @@ public class SalesMngController { return resultMap; } + + /** + * 견적요청서 메일 발송 팝업 + */ + @RequestMapping("/salesMng/quotationRequestMailPopup.do") + public String quotationRequestMailPopup(HttpServletRequest request, Model model){ + return "/salesMng/quotationRequestMailPopup"; + } + + /** + * 견적요청서 정보 조회 (메일 발송용) + */ + @ResponseBody + @RequestMapping("/salesMng/getQuotationRequestInfoForMail.do") + public Map getQuotationRequestInfoForMail(HttpServletRequest request, @RequestParam Map paramMap){ + return salesMngService.getQuotationRequestInfoForMail(request, paramMap); + } + + /** + * 견적요청서 메일 발송 + */ + @ResponseBody + @RequestMapping("/salesMng/sendQuotationRequestMail.do") + public Map sendQuotationRequestMail(HttpServletRequest request, @RequestParam Map paramMap){ + return salesMngService.sendQuotationRequestMail(request, paramMap); + } } diff --git a/src/com/pms/salesmgmt/service/SalesMngService.java b/src/com/pms/salesmgmt/service/SalesMngService.java index 5daba64..8b26ec2 100644 --- a/src/com/pms/salesmgmt/service/SalesMngService.java +++ b/src/com/pms/salesmgmt/service/SalesMngService.java @@ -37,6 +37,7 @@ import com.pms.common.SqlMapConfig; import com.pms.common.bean.PersonBean; import com.pms.common.utils.CommonUtils; import com.pms.common.utils.Constants; +import com.pms.common.utils.MailUtil; import com.pms.service.CommonService; /** @@ -2581,4 +2582,123 @@ public class SalesMngService { } return resultList; } + + /** + * 견적요청서 정보 조회 (메일 발송용) + */ + public Map getQuotationRequestInfoForMail(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + // 견적요청서 마스터 정보 조회 + Map masterInfo = (Map)sqlSession.selectOne("salesMng.getQuotationRequestMasterInfo", paramMap); + + if(masterInfo != null) { + // 상세 품목 목록 조회 + List items = sqlSession.selectList("salesMng.getQuotationRequestDetailList", paramMap); + masterInfo.put("ITEMS", items); + + resultMap.put("info", masterInfo); + resultMap.put("resultFlag", "S"); + } else { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "견적요청서 정보를 찾을 수 없습니다."); + } + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "조회 중 오류가 발생했습니다."); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 견적요청서 메일 발송 + */ + public Map sendQuotationRequestMail(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + String quotationMasterObjid = CommonUtils.checkNull(paramMap.get("QUOTATION_REQUEST_MASTER_OBJID")); + String toEmails = CommonUtils.checkNull(paramMap.get("toEmails")); + String ccEmails = CommonUtils.checkNull(paramMap.get("ccEmails")); + String subject = CommonUtils.checkNull(paramMap.get("subject")); + String contents = CommonUtils.checkNull(paramMap.get("contents")); + + // 수신인 이메일 파싱 + ArrayList toEmailList = new ArrayList(); + if(!toEmails.isEmpty()) { + String[] emails = toEmails.split(","); + for(String email : emails) { + email = email.trim(); + if(!email.isEmpty()) { + toEmailList.add(email); + } + } + } + + // 참조 이메일 파싱 + ArrayList ccEmailList = null; + if(!ccEmails.isEmpty()) { + ccEmailList = new ArrayList(); + String[] emails = ccEmails.split(","); + for(String email : emails) { + email = email.trim(); + if(!email.isEmpty()) { + ccEmailList.add(email); + } + } + } + + if(toEmailList.isEmpty()) { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "수신인 이메일이 없습니다."); + return resultMap; + } + + // 줄바꿈을 HTML
태그로 변환 + String htmlContents = contents.replace("\n", "
"); + + // 메일 발송 (간단한 방식) + boolean sendResult = false; + for(String toEmail : toEmailList) { + sendResult = MailUtil.sendMailNew(toEmail, subject, htmlContents); + if(!sendResult) break; + } + + if(sendResult) { + // 메일 발송 상태 업데이트 + Map updateParam = new HashMap(); + updateParam.put("QUOTATION_REQUEST_MASTER_OBJID", quotationMasterObjid); + updateParam.put("MAIL_SEND_YN", "Y"); + sqlSession.update("salesMng.updateQuotationRequestMailSent", updateParam); + + sqlSession.commit(); + + resultMap.put("resultFlag", "S"); + resultMap.put("message", "메일이 발송되었습니다."); + } else { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "메일 발송에 실패했습니다."); + } + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "메일 발송 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } } -- 2.49.1 From f3823dae40dc30fa3d4e5d56068b41a35a940da1 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 19 Jan 2026 16:41:44 +0900 Subject: [PATCH 3/5] =?UTF-8?q?=EA=B5=AC=EB=A7=A4=EC=9A=94=EC=B2=AD=5F?= =?UTF-8?q?=ED=92=88=EC=9D=98=EC=84=9C=EA=B4=80=EB=A6=AC=20=EA=B2=B0?= =?UTF-8?q?=EC=9E=AC=EC=99=84=EB=A3=8C=EC=8B=9C=20->=20=EA=B5=AC=EB=A7=A4?= =?UTF-8?q?=EA=B4=80=EB=A6=AC=5F=ED=92=88=EC=9D=98=EC=84=9C=EA=B4=80?= =?UTF-8?q?=EB=A6=AC=EC=97=90,,=20=EA=B7=B8=20=ED=9B=84=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EC=84=B8=EC=8A=A4=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../salesMng/purchaseRegProposalMngList.jsp | 227 +++++++++++++ .../view/salesMng/purchaseRequestRegList.jsp | 58 ++-- src/com/pms/mapper/purchaseOrder.xml | 15 +- src/com/pms/mapper/salesMng.xml | 297 +++++++++++++++++- .../controller/SalesMngController.java | 36 +++ .../salesmgmt/service/SalesMngService.java | 193 +++++++++++- src/com/pms/service/PurchaseOrderService.java | 31 +- 7 files changed, 796 insertions(+), 61 deletions(-) create mode 100644 WebContent/WEB-INF/view/salesMng/purchaseRegProposalMngList.jsp diff --git a/WebContent/WEB-INF/view/salesMng/purchaseRegProposalMngList.jsp b/WebContent/WEB-INF/view/salesMng/purchaseRegProposalMngList.jsp new file mode 100644 index 0000000..d351bd7 --- /dev/null +++ b/WebContent/WEB-INF/view/salesMng/purchaseRegProposalMngList.jsp @@ -0,0 +1,227 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> + + +<% +// DB에서 메뉴명 조회 (공통 유틸 사용) +String menuObjId = request.getParameter("menuObjId"); +String menuName = CommonUtils.getMenuName(menuObjId, "구매요청_품의서관리"); +%> + + + + +<%=Constants.SYSTEM_NAME%> + + + + +
+ +
+
+
+
+

+ <%=menuName%> +

+
+ + +
+
+ +
+ + + + + + + + + + + + + +
+ + + + + + + ~ + +
+
+ + <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %> +
+
+
+
+ + + diff --git a/WebContent/WEB-INF/view/salesMng/purchaseRequestRegList.jsp b/WebContent/WEB-INF/view/salesMng/purchaseRequestRegList.jsp index 10fdff3..73bcfd5 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseRequestRegList.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseRequestRegList.jsp @@ -203,28 +203,8 @@ var columns = [ fn_openSalesRequestFormPopUp(data.OBJID); } } - ,{headerHozAlign : 'center', hozAlign : 'center', title : "요청인", field :"REQUEST_USER_NAME" , widthGrow:1.1, - // 요청인: 구매요청서 작성 시에만 표시 - formatter: function(cell, formatterParams, onRendered){ - var data = cell.getData(); - var hasPurchaseRequest = fnc_checkNull(data.HAS_PURCHASE_REQUEST); - if(hasPurchaseRequest == 'Y') { - return fnc_checkNull(data.REQUEST_USER_NAME); - } - return '-'; - } - } - ,{headerHozAlign : 'center', hozAlign : 'center', title : "입고요청일", field :"DELIVERY_REQUEST_DATE" , widthGrow:1.1, - // 입고요청일: 구매요청서 작성 시에만 표시 - formatter: function(cell, formatterParams, onRendered){ - var data = cell.getData(); - var hasPurchaseRequest = fnc_checkNull(data.HAS_PURCHASE_REQUEST); - if(hasPurchaseRequest == 'Y') { - return fnc_checkNull(data.DELIVERY_REQUEST_DATE); - } - return '-'; - } - } + ,{headerHozAlign : 'center', hozAlign : 'center', title : "요청인", field :"REQUEST_USER_NAME" , widthGrow:1.1 } + ,{headerHozAlign : 'center', hozAlign : 'center', title : "입고요청일", field :"DELIVERY_REQUEST_DATE" , widthGrow:1.1 } ,{headerHozAlign : 'center', hozAlign : 'center', title : "작성일", field :"REGDATE_TITLE" , widthGrow:1.1 } ,{headerHozAlign : 'center', hozAlign : 'center', title : "상태", field :"STATUS_TITLE" , widthGrow:1.1 } ]; @@ -480,10 +460,9 @@ function fn_formPopUp(objId,sales_request_objid){ } /** - * 품의서 생성 함수 - * - 선택된 구매요청서에서 단가가 입력된 품목만 필터링 + * 품의서 생성 함수 (구매요청서 전용) + * - 선택된 구매요청서에서 단가+공급업체 입력된 품목만 필터링 * - 이미 품의서가 생성된 품목은 제외 - * - 하나의 품의서로 생성 */ function fn_createProposal() { // 1. 선택된 행 확인 @@ -510,9 +489,9 @@ function fn_createProposal() { var selectedRow = selectedRows[0]; var salesRequestObjid = fnc_checkNull(selectedRow.OBJID); - // 2. 구매요청서의 품목 중 단가가 입력되고 품의서 미생성된 품목 조회 + // 2. 구매요청서 품의서 대상 품목 조회 (전용 API) $.ajax({ - url: "/salesMng/getProposalTargetParts.do", + url: "/salesMng/getProposalTargetPartsFromPurchaseReg.do", type: "POST", data: { SALES_REQUEST_MASTER_OBJID: salesRequestObjid @@ -521,13 +500,13 @@ function fn_createProposal() { success: function(response) { if(response.resultFlag === "S") { var targetParts = response.data; - var excludedParts = response.excludedParts || []; // 공급업체 미입력 품목 + var excludedParts = response.excludedParts || []; // 3. 대상 품목 확인 if(!targetParts || targetParts.length == 0) { Swal.fire({ title: '알림', - text: '품의서를 생성할 품목이 없습니다.\n(단가와 공급업체가 모두 입력되고 품의서가 생성되지 않은 품목만 대상)', + text: '품의서를 생성할 품목이 없습니다.\n(단가+공급업체 입력, 품의서 미생성 품목만 대상)', icon: 'info' }); return; @@ -536,14 +515,14 @@ function fn_createProposal() { // 4. 품의서 생성 확인 var partCount = targetParts.length; var partListHtml = targetParts.map(function(part) { - return '- ' + fnc_checkNull(part.PART_NO) + ' / ' + fnc_checkNull(part.PART_NAME); + return '- ' + fnc_checkNull(part.PART_NO || part.part_no) + ' / ' + fnc_checkNull(part.PART_NAME || part.part_name); }).join('
'); // 공급업체 미입력으로 제외된 품목이 있는 경우 알림 추가 var excludedHtml = ''; if(excludedParts && excludedParts.length > 0) { var excludedListHtml = excludedParts.map(function(part) { - return '- ' + fnc_checkNull(part.PART_NO) + ' / ' + fnc_checkNull(part.PART_NAME); + return '- ' + fnc_checkNull(part.PART_NO || part.part_no) + ' / ' + fnc_checkNull(part.PART_NAME || part.part_name); }).join('
'); excludedHtml = '
' + @@ -569,7 +548,7 @@ function fn_createProposal() { cancelButtonText: '취소' }).then((result) => { if (result.isConfirmed) { - fn_executeCreateProposal(salesRequestObjid, targetParts); + fn_executeCreateProposal(salesRequestObjid); } }); } else { @@ -579,7 +558,7 @@ function fn_createProposal() { if(excludedParts && excludedParts.length > 0) { var excludedListHtml = excludedParts.map(function(part) { - return '- ' + fnc_checkNull(part.PART_NO) + ' / ' + fnc_checkNull(part.PART_NAME); + return '- ' + fnc_checkNull(part.PART_NO || part.part_no) + ' / ' + fnc_checkNull(part.PART_NAME || part.part_name); }).join('
'); excludedHtml = '

' + @@ -600,7 +579,7 @@ function fn_createProposal() { error: function(xhr, status, error) { Swal.fire({ title: '오류', - text: '서버 통신 중 오류가 발생했습니다.22', + text: '서버 통신 중 오류가 발생했습니다.', icon: 'error' }); } @@ -608,22 +587,21 @@ function fn_createProposal() { } /** - * 품의서 생성 실행 + * 품의서 생성 실행 (구매요청서 전용) */ -function fn_executeCreateProposal(salesRequestObjid, targetParts) { +function fn_executeCreateProposal(salesRequestObjid) { $.ajax({ - url: "/salesMng/createProposalFromPurchaseList.do", + url: "/salesMng/createProposalFromPurchaseReg.do", type: "POST", data: { - SALES_REQUEST_MASTER_OBJID: salesRequestObjid, - TARGET_PARTS: JSON.stringify(targetParts) + SALES_REQUEST_MASTER_OBJID: salesRequestObjid }, dataType: "json", success: function(response) { if(response.resultFlag === "S") { Swal.fire({ title: '생성 완료', - text: '품의서가 생성되었습니다.\n품의서 관리에서 확인하세요.', + html: response.message || '품의서가 생성되었습니다.', icon: 'success' }).then(() => { fn_search(); // 목록 새로고침 diff --git a/src/com/pms/mapper/purchaseOrder.xml b/src/com/pms/mapper/purchaseOrder.xml index b509da5..c580968 100644 --- a/src/com/pms/mapper/purchaseOrder.xml +++ b/src/com/pms/mapper/purchaseOrder.xml @@ -879,10 +879,15 @@ SELECT SRP.OBJID, SRP.PART_OBJID, - SRP.QTY, - COALESCE(SRP.PO_QTY, SRP.QTY::NUMERIC, 0) AS ORDER_QTY, -- 발주수량 (PO_QTY 우선, 없으면 QTY) - SRP.UNIT_PRICE AS PARTNER_PRICE, - SRP.TOTAL_PRICE, + COALESCE(NULLIF(SRP.QTY::VARCHAR, ''), '0') AS QTY, + -- 발주수량: PO_QTY가 있고 0보다 크면 사용, 아니면 QTY 사용 + CASE + WHEN SRP.PO_QTY IS NOT NULL AND SRP.PO_QTY::NUMERIC > 0 THEN SRP.PO_QTY::VARCHAR + WHEN SRP.QTY IS NOT NULL THEN SRP.QTY::VARCHAR + ELSE '0' + END AS ORDER_QTY, + COALESCE(SRP.UNIT_PRICE, 0) AS PARTNER_PRICE, + COALESCE(SRP.TOTAL_PRICE, 0) AS TOTAL_PRICE, SRP.VENDOR_PM AS PARTNER_OBJID, PM.PART_NO, PM.PART_NAME, @@ -3317,7 +3322,7 @@ SELECT POM.OBJID ON POM.CONTRACT_MGMT_OBJID = CM.OBJID left outer join SALES_REQUEST_MASTER as SRM on POM.SALES_REQUEST_OBJID = SRM.OBJID - and SRM.DOC_TYPE = 'PROPOSAL' + and SRM.DOC_TYPE IN ('PROPOSAL', 'PURCHASE_REG_PROPOSAL') + AND ( + SRM.DOC_TYPE = 'PROPOSAL' + OR (SRM.DOC_TYPE = 'PURCHASE_REG_PROPOSAL' AND A.ROUTE_STATUS = 'complete') + ) AND SRM.REQUEST_MNG_NO LIKE '%${SEARCH_PROPOSAL_NO}%' @@ -4138,6 +4143,130 @@ ORDER BY V.PATH2 ORDER BY SRM.REGDATE DESC + + + + SELECT + SRP.OBJID, + SRP.PART_OBJID, + PM.PART_NO, + PM.PART_NAME, + SRP.QTY, + COALESCE(SRP.UNIT_PRICE, NULLIF(SRP.PARTNER_PRICE, '')::NUMERIC, 0) AS UNIT_PRICE, + SRP.TOTAL_PRICE, + COALESCE(SRP.VENDOR_PM, SRP.PARTNER_OBJID) AS VENDOR_PM, + CASE + WHEN COALESCE(SRP.VENDOR_PM, SRP.PARTNER_OBJID) LIKE 'C_%' THEN (SELECT CLIENT_NM FROM CLIENT_MNG WHERE 'C_' || OBJID::VARCHAR = COALESCE(SRP.VENDOR_PM, SRP.PARTNER_OBJID)) + ELSE (SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID::VARCHAR = COALESCE(SRP.VENDOR_PM, SRP.PARTNER_OBJID)) + END AS VENDOR_NAME, + SRP.NET_QTY, + SRP.PO_QTY + FROM + SALES_REQUEST_PART SRP + LEFT JOIN PART_MNG PM ON SRP.PART_OBJID::VARCHAR = PM.OBJID::VARCHAR + WHERE + SRP.SALES_REQUEST_MASTER_OBJID = #{SALES_REQUEST_MASTER_OBJID} + -- 단가가 입력되어 있고 (UNIT_PRICE 또는 PARTNER_PRICE) + AND ( + (SRP.UNIT_PRICE IS NOT NULL AND SRP.UNIT_PRICE > 0) + OR (SRP.PARTNER_PRICE IS NOT NULL AND SRP.PARTNER_PRICE != '' AND SRP.PARTNER_PRICE::NUMERIC > 0) + ) + -- 공급업체가 입력되어 있고 (VENDOR_PM 또는 PARTNER_OBJID) + AND ( + (SRP.VENDOR_PM IS NOT NULL AND SRP.VENDOR_PM != '') + OR (SRP.PARTNER_OBJID IS NOT NULL AND SRP.PARTNER_OBJID != '') + ) + -- 품의서가 생성되지 않은 품목만 + AND SRP.PROPOSAL_DATE IS NULL + ORDER BY SRP.REGDATE + + + + + + + + INSERT INTO SALES_REQUEST_MASTER ( + OBJID, + REQUEST_MNG_NO, + PROJECT_NO, + PURCHASE_TYPE, + ORDER_TYPE, + PRODUCT_NAME, + AREA_CD, + CUSTOMER_OBJID, + PAID_TYPE, + REQUEST_USER_ID, + DELIVERY_REQUEST_DATE, + STATUS, + WRITER, + REGDATE, + DOC_TYPE + ) VALUES ( + #{OBJID}, + #{REQUEST_MNG_NO}, + #{PROJECT_NO}, + #{PURCHASE_TYPE}, + #{ORDER_TYPE}, + #{PRODUCT_NAME}, + #{AREA_CD}, + #{CUSTOMER_OBJID}, + #{PAID_TYPE}, + #{REQUEST_USER_ID}, + #{DELIVERY_REQUEST_DATE}, + 'create', + #{WRITER}, + NOW(), + 'PURCHASE_REG_PROPOSAL' + ) + + + + + INSERT INTO SALES_REQUEST_PART ( + OBJID, + SALES_REQUEST_MASTER_OBJID, + PART_OBJID, + QTY, + UNIT_PRICE, + TOTAL_PRICE, + VENDOR_PM, + NET_QTY, + PO_QTY, + USE_YN, + PROPOSAL_DATE, + WRITER, + REGDATE + ) + SELECT + #{NEW_OBJID}, + #{PROPOSAL_MASTER_OBJID}, + PART_OBJID, + QTY, + CASE + WHEN UNIT_PRICE IS NOT NULL AND UNIT_PRICE > 0 THEN UNIT_PRICE + WHEN PARTNER_PRICE IS NOT NULL AND PARTNER_PRICE != '' THEN PARTNER_PRICE::NUMERIC + ELSE 0 + END AS UNIT_PRICE, + CASE + WHEN TOTAL_PRICE IS NOT NULL AND TOTAL_PRICE > 0 THEN TOTAL_PRICE + ELSE COALESCE(QTY::NUMERIC, 0) * + CASE + WHEN UNIT_PRICE IS NOT NULL AND UNIT_PRICE > 0 THEN UNIT_PRICE + WHEN PARTNER_PRICE IS NOT NULL AND PARTNER_PRICE != '' THEN PARTNER_PRICE::NUMERIC + ELSE 0 + END + END AS TOTAL_PRICE, + COALESCE(NULLIF(VENDOR_PM, ''), PARTNER_OBJID) AS VENDOR_PM, + NET_QTY, + COALESCE(QTY::NUMERIC, 0) AS PO_QTY, -- 발주수량에 QTY 값 저장 + USE_YN, + NOW() AS PROPOSAL_DATE, + #{WRITER}, + NOW() + FROM SALES_REQUEST_PART + WHERE OBJID = #{SOURCE_OBJID} + + + + + UPDATE SALES_REQUEST_PART + SET PROPOSAL_DATE = NOW() + WHERE OBJID IN + + #{objid} + + + \ No newline at end of file diff --git a/src/com/pms/salesmgmt/controller/SalesMngController.java b/src/com/pms/salesmgmt/controller/SalesMngController.java index 59fee7e..52110b0 100644 --- a/src/com/pms/salesmgmt/controller/SalesMngController.java +++ b/src/com/pms/salesmgmt/controller/SalesMngController.java @@ -1337,6 +1337,24 @@ public class SalesMngController { return paramMap; } + /** + * 구매요청_품의서 관리 - 목록 화면 + */ + @RequestMapping("/salesMng/purchaseRegProposalMngList.do") + public String purchaseRegProposalMngList(HttpServletRequest request, @RequestParam Map paramMap){ + return "/salesMng/purchaseRegProposalMngList"; + } + + /** + * 구매요청_품의서 관리 - 목록 조회 (페이징) + */ + @ResponseBody + @RequestMapping("/salesMng/purchaseRegProposalMngGridList.do") + public Map getPurchaseRegProposalMngGridList(HttpServletRequest request, @RequestParam Map paramMap){ + commonService.selectListPagingNew("salesMng.getPurchaseRegProposalMngGridList", request, paramMap); + return paramMap; + } + /** * 품의서 상세 팝업 * @param request @@ -1783,4 +1801,22 @@ public class SalesMngController { public Map sendQuotationRequestMail(HttpServletRequest request, @RequestParam Map paramMap){ return salesMngService.sendQuotationRequestMail(request, paramMap); } + + /** + * 구매요청서 품의서 대상 품목 조회 + */ + @ResponseBody + @RequestMapping("/salesMng/getProposalTargetPartsFromPurchaseReg.do") + public Map getProposalTargetPartsFromPurchaseReg(HttpServletRequest request, @RequestParam Map paramMap){ + return salesMngService.getProposalTargetPartsFromPurchaseReg(request, paramMap); + } + + /** + * 구매요청서 품의서 생성 + */ + @ResponseBody + @RequestMapping("/salesMng/createProposalFromPurchaseReg.do") + public Map createProposalFromPurchaseReg(HttpServletRequest request, @RequestParam Map paramMap){ + return salesMngService.createProposalFromPurchaseReg(request, paramMap); + } } diff --git a/src/com/pms/salesmgmt/service/SalesMngService.java b/src/com/pms/salesmgmt/service/SalesMngService.java index 8b26ec2..fe9ca0f 100644 --- a/src/com/pms/salesmgmt/service/SalesMngService.java +++ b/src/com/pms/salesmgmt/service/SalesMngService.java @@ -13,6 +13,7 @@ import java.io.FileInputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -1974,6 +1975,10 @@ public class SalesMngService { List materialExcludedParts = null; // 소재 제외 (공급업체 미입력) List processingExcludedParts = null; // 가공 제외 (가공업체 미입력) + // DOC_TYPE 확인 (PURCHASE_REG는 가공 컬럼이 없음) + String docType = CommonUtils.checkNull(purchaseRequestInfo.get("DOC_TYPE")); + boolean isPurchaseReg = "PURCHASE_REG".equals(docType); + if(!mbomHeaderObjid.isEmpty()) { // M-BOM 기반 -> MBOM_DETAIL에서 조회 Map mbomParam = new HashMap(); @@ -1992,9 +1997,14 @@ public class SalesMngService { materialTargetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromManual", paramMap); materialExcludedParts = sqlSession.selectList("salesMng.getProposalExcludedPartsFromManual", paramMap); - // 가공 품의서 대상 - processingTargetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromManualProcessing", paramMap); - processingExcludedParts = sqlSession.selectList("salesMng.getProposalExcludedPartsFromManualProcessing", paramMap); + // 가공 품의서 대상 (PURCHASE_REG는 가공 컬럼이 없으므로 제외) + if(!isPurchaseReg) { + processingTargetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromManualProcessing", paramMap); + processingExcludedParts = sqlSession.selectList("salesMng.getProposalExcludedPartsFromManualProcessing", paramMap); + } else { + processingTargetParts = new ArrayList(); + processingExcludedParts = new ArrayList(); + } } materialTargetParts = CommonUtils.keyChangeUpperList(materialTargetParts); @@ -2138,6 +2148,11 @@ public class SalesMngService { proposalMaster.put("WRITER", userId); proposalMaster.put("PROPOSAL_TYPE", proposalType); // 소재/가공 구분 + // 원본 DOC_TYPE에 따라 품의서 DOC_TYPE 결정 + String sourceDocType = CommonUtils.checkNull(purchaseRequestInfo.get("DOC_TYPE")); + String proposalDocType = "PURCHASE_REG".equals(sourceDocType) ? "PURCHASE_REG_PROPOSAL" : "PROPOSAL"; + proposalMaster.put("DOC_TYPE", proposalDocType); + sqlSession.insert("salesMng.insertProposal", proposalMaster); // 3. 품의서용 SALES_REQUEST_PART 생성 (대상 품목 복사) @@ -2701,4 +2716,176 @@ public class SalesMngService { return resultMap; } + + /** + * 구매요청서 품의서 대상 품목 조회 + */ + public Map getProposalTargetPartsFromPurchaseReg(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + String salesRequestMasterObjid = CommonUtils.checkNull(paramMap.get("SALES_REQUEST_MASTER_OBJID")); + + Map param = new HashMap(); + param.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + + // 대상 품목 조회 + List targetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromPurchaseReg", param); + // 제외 품목 조회 (공급업체 미입력) + List excludedParts = sqlSession.selectList("salesMng.getProposalExcludedPartsFromPurchaseReg", param); + + if(targetParts != null && !targetParts.isEmpty()) { + resultMap.put("resultFlag", "S"); + resultMap.put("data", targetParts); + resultMap.put("excludedParts", excludedParts); + } else { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "품의서 생성 대상 품목이 없습니다.\n(단가+공급업체 입력, 품의서 미생성 품목만 대상)"); + resultMap.put("excludedParts", excludedParts); + } + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "조회 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } + + /** + * 구매요청서 품의서 생성 (PURCHASE_REG 전용) - 업체별로 분리 생성 + */ + public Map createProposalFromPurchaseReg(HttpServletRequest request, Map paramMap) { + SqlSession sqlSession = null; + Map resultMap = new HashMap(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + HttpSession session = request.getSession(); + PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String userId = CommonUtils.checkNull(person.getUserId()); + + String salesRequestMasterObjid = CommonUtils.checkNull(paramMap.get("SALES_REQUEST_MASTER_OBJID")); + + if(salesRequestMasterObjid.isEmpty()) { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "구매요청서 정보가 없습니다."); + return resultMap; + } + + // 1. 구매요청서 마스터 정보 조회 + Map masterParam = new HashMap(); + masterParam.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid); + Map purchaseRequestInfo = (Map)sqlSession.selectOne("salesMng.getSalesRequestMasterInfo", masterParam); + purchaseRequestInfo = CommonUtils.toUpperCaseMapKey(purchaseRequestInfo); + + // 2. 대상 품목 조회 + List targetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromPurchaseReg", masterParam); + + if(targetParts == null || targetParts.isEmpty()) { + resultMap.put("resultFlag", "F"); + resultMap.put("message", "품의서 생성 대상 품목이 없습니다."); + return resultMap; + } + + // 3. 업체별로 품목 그룹화 + Map> partsByVendor = new LinkedHashMap>(); + for(Map part : targetParts) { + String vendorPm = CommonUtils.checkNull(part.get("VENDOR_PM")); + if(vendorPm.isEmpty()) { + vendorPm = CommonUtils.checkNull(part.get("vendor_pm")); + } + + if(!partsByVendor.containsKey(vendorPm)) { + partsByVendor.put(vendorPm, new ArrayList()); + } + partsByVendor.get(vendorPm).add(part); + } + + // 4. 업체별로 품의서 생성 + List createdProposalNos = new ArrayList(); + List allSourceObjids = new ArrayList(); + + for(String vendorPm : partsByVendor.keySet()) { + List vendorParts = partsByVendor.get(vendorPm); + + // 품의서 번호 생성 + String proposalNo = (String)sqlSession.selectOne("salesMng.getNextProposalNo"); + createdProposalNos.add(proposalNo); + + // 품의서 마스터 생성 + Map proposalMaster = new HashMap(); + String proposalMasterObjid = CommonUtils.createObjId(); + proposalMaster.put("OBJID", proposalMasterObjid); + proposalMaster.put("REQUEST_MNG_NO", proposalNo); + proposalMaster.put("PROJECT_NO", purchaseRequestInfo.get("PROJECT_NO")); + proposalMaster.put("PURCHASE_TYPE", purchaseRequestInfo.get("PURCHASE_TYPE")); + proposalMaster.put("ORDER_TYPE", purchaseRequestInfo.get("ORDER_TYPE")); + proposalMaster.put("PRODUCT_NAME", purchaseRequestInfo.get("PRODUCT_NAME")); + proposalMaster.put("AREA_CD", purchaseRequestInfo.get("AREA_CD")); + proposalMaster.put("CUSTOMER_OBJID", purchaseRequestInfo.get("CUSTOMER_OBJID")); + proposalMaster.put("PAID_TYPE", purchaseRequestInfo.get("PAID_TYPE")); + proposalMaster.put("DELIVERY_REQUEST_DATE", purchaseRequestInfo.get("DELIVERY_REQUEST_DATE")); + proposalMaster.put("REQUEST_USER_ID", userId); + proposalMaster.put("WRITER", userId); + + sqlSession.insert("salesMng.insertProposalFromPurchaseReg", proposalMaster); + + // 해당 업체의 품목 생성 + for(Map part : vendorParts) { + String sourceObjid = CommonUtils.checkNull(part.get("OBJID")); + if(sourceObjid.isEmpty()) { + sourceObjid = CommonUtils.checkNull(part.get("objid")); + } + allSourceObjids.add(sourceObjid); + + Map partParam = new HashMap(); + partParam.put("NEW_OBJID", CommonUtils.createObjId()); + partParam.put("PROPOSAL_MASTER_OBJID", proposalMasterObjid); + partParam.put("SOURCE_OBJID", sourceObjid); + partParam.put("WRITER", userId); + + sqlSession.insert("salesMng.insertProposalPartFromPurchaseReg", partParam); + } + } + + // 5. 원본 품목 PROPOSAL_DATE 업데이트 + if(!allSourceObjids.isEmpty()) { + Map updateParam = new HashMap(); + updateParam.put("PART_OBJIDS", allSourceObjids); + sqlSession.update("salesMng.updateProposalDateForPurchaseRegParts", updateParam); + } + + sqlSession.commit(); + + // 결과 메시지 생성 + String message = "품의서가 " + createdProposalNos.size() + "건 생성되었습니다.\n"; + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < createdProposalNos.size(); i++) { + if(i > 0) sb.append(", "); + sb.append(createdProposalNos.get(i)); + } + message += sb.toString(); + + resultMap.put("resultFlag", "S"); + resultMap.put("message", message); + resultMap.put("proposalNos", createdProposalNos); + + } catch(Exception e) { + if(sqlSession != null) sqlSession.rollback(); + e.printStackTrace(); + resultMap.put("resultFlag", "F"); + resultMap.put("message", "품의서 생성 중 오류가 발생했습니다: " + e.getMessage()); + } finally { + if(sqlSession != null) sqlSession.close(); + } + + return resultMap; + } } diff --git a/src/com/pms/service/PurchaseOrderService.java b/src/com/pms/service/PurchaseOrderService.java index 9b79488..056b096 100644 --- a/src/com/pms/service/PurchaseOrderService.java +++ b/src/com/pms/service/PurchaseOrderService.java @@ -1839,8 +1839,9 @@ public class PurchaseOrderService { String docType = CommonUtils.checkNull((String) salesRequestInfo.get("doc_type")); System.out.println("docType: " + docType); - // 품의서(PROPOSAL)인 경우 VENDOR_PM 사용, 구매요청서는 PARTNER_OBJID 사용 - String queryId = "PROPOSAL".equals(docType) + // 품의서(PROPOSAL, PURCHASE_REG_PROPOSAL)인 경우 VENDOR_PM 사용, 구매요청서는 PARTNER_OBJID 사용 + boolean isProposal = "PROPOSAL".equals(docType) || "PURCHASE_REG_PROPOSAL".equals(docType); + String queryId = isProposal ? "purchaseOrder.getVendorListByProposalObjid" : "purchaseOrder.grtPartnerObjidBySalesRequestObjid"; System.out.println("queryId: " + queryId); @@ -1881,7 +1882,7 @@ public class PurchaseOrderService { sqlSession.update("purchaseOrder.mergePurchaseOrderMasterBySalesRequest", masterParam); // 해당 공급업체의 품목 조회 - String partQueryId = "PROPOSAL".equals(docType) + String partQueryId = isProposal ? "purchaseOrder.getProposalPartsByVendor" : "purchaseOrder.getSalesRequestPartsByVendor"; @@ -1898,16 +1899,28 @@ public class PurchaseOrderService { partParam.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjid); partParam.put("PART_OBJID", CommonUtils.checkNull((String) partMap.get("part_objid"))); - // 수량 처리 - String qtyStr = CommonUtils.checkNull(partMap.get("qty"), "0"); - String orderQtyStr = CommonUtils.checkNull(partMap.get("order_qty"), "0"); + // 수량 처리 (Object 타입 대응) + Object qtyObj = partMap.get("qty"); + Object orderQtyObj = partMap.get("order_qty"); + String qtyStr = (qtyObj != null) ? String.valueOf(qtyObj).trim() : "0"; + if("".equals(qtyStr)) qtyStr = "0"; + + String orderQtyStr = (orderQtyObj != null) ? String.valueOf(orderQtyObj).trim() : ""; + // ORDER_QTY가 없거나 빈값/0이면 QTY 사용 + if("".equals(orderQtyStr) || "0".equals(orderQtyStr)) { + orderQtyStr = qtyStr; + } partParam.put("QTY", qtyStr); partParam.put("ORDER_QTY", orderQtyStr); - // 단가 정보 - String partnerPrice = CommonUtils.checkNull(partMap.get("partner_price"), "0"); - String totalPrice = CommonUtils.checkNull(partMap.get("total_price"), "0"); + // 단가 정보 (Object 타입 대응) + Object partnerPriceObj = partMap.get("partner_price"); + Object totalPriceObj = partMap.get("total_price"); + String partnerPrice = (partnerPriceObj != null) ? String.valueOf(partnerPriceObj).trim() : "0"; + if("".equals(partnerPrice)) partnerPrice = "0"; + String totalPrice = (totalPriceObj != null) ? String.valueOf(totalPriceObj).trim() : "0"; + if("".equals(totalPrice)) totalPrice = "0"; partParam.put("PARTNER_PRICE", partnerPrice); -- 2.49.1 From 3a2075fa2bb2eab8d025fb753b63ad97668ab115 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 19 Jan 2026 18:05:26 +0900 Subject: [PATCH 4/5] =?UTF-8?q?=ED=92=88=EB=AA=A9=EB=B3=84=20=EC=9E=85?= =?UTF-8?q?=EA=B3=A0=EA=B4=80=EB=A6=AC=20=EB=A9=94=EB=89=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../deliveryMngAcceptancePartList.jsp | 406 ++++++++++++++++++ .../controller/PurchaseOrderController.java | 47 ++ src/com/pms/mapper/purchaseOrder.xml | 161 +++++++ 3 files changed, 614 insertions(+) create mode 100644 WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptancePartList.jsp diff --git a/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptancePartList.jsp b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptancePartList.jsp new file mode 100644 index 0000000..e0f054e --- /dev/null +++ b/WebContent/WEB-INF/view/purchaseOrder/deliveryMngAcceptancePartList.jsp @@ -0,0 +1,406 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> + + +<% +// DB에서 메뉴명 조회 (공통 유틸 사용) +String menuObjId = request.getParameter("menuObjId"); +String menuName = CommonUtils.getMenuName(menuObjId, "품목별 입고 관리"); +%> + + + + + <%=Constants.SYSTEM_NAME%> + + + + + + +
+ + + + + +
+
+ + +
+
+
+

+ <%=menuName%> +

+
+ + + +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ ~ + + + ~ + + + + + + +
+
+ + <%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %> +
+
+
+ + diff --git a/src/com/pms/controller/PurchaseOrderController.java b/src/com/pms/controller/PurchaseOrderController.java index 09e3374..ba8522b 100644 --- a/src/com/pms/controller/PurchaseOrderController.java +++ b/src/com/pms/controller/PurchaseOrderController.java @@ -1341,6 +1341,53 @@ public class PurchaseOrderController { return purchaseOrderService.updatePurchaseCloseDate(objIds); } + /** + * 품목별 입고 관리 화면 + */ + @RequestMapping("/purchaseOrder/deliveryMngAcceptancePartList.do") + public String deliveryMngAcceptancePartList(HttpServletRequest request, @RequestParam Map paramMap){ + ArrayList list = new ArrayList(); + Map code_map = new HashMap(); + + try{ + // 고객사 + code_map.put("customer_cd", commonService.bizMakeOptionList("", (String)paramMap.get("customer_cd"), "common.getsupplyselect")); + // 고객사프로젝트명 + code_map.put("customer_project_name", commonService.bizMakeOptionList("", (String)paramMap.get("customer_project_name"), "common.getsupplyselect")); + // 당사프로젝트번호 + code_map.put("project_no", commonService.bizMakeOptionList("", (String)paramMap.get("project_no"), "common.getProjectNameList")); + // PART구분 + code_map.put("part_type", commonService.bizMakeOptionList(Constants.PART_TYPE_CODE, (String)paramMap.get("part_type"), "common.getCodeselect")); + // 발주구분 + code_map.put("type", commonService.bizMakeOptionList("0001068", (String)paramMap.get("type"), "common.getCodeselect")); + // 공급업체 (공급업체 + 일반거래처 통합) + code_map.put("partner_objid", commonService.bizMakeOptionList("", (String)paramMap.get("partner_objid"), "common.getAllSupplySelect")); + // 조치담당자 + code_map.put("sales_mng_user_id", commonService.bizMakeOptionList("", (String)paramMap.get("sales_mng_user_id"), "common.getUserselect")); + // 상태코드 + code_map.put("appr_status", commonService.bizMakeOptionList("0000099", (String)paramMap.get("appr_status"), "common.getCodeselect")); + + list = commonService.selectListPaging("purchaseOrder.deliveryMngPartList", request, paramMap); + }catch(Exception e){ + e.printStackTrace(); + } + + request.setAttribute("code_map", code_map); + request.setAttribute("LIST", JsonUtil.ListToJson(list)); + + return "/purchaseOrder/deliveryMngAcceptancePartList"; + } + + /** + * 품목별 입고 관리 그리드 조회 + */ + @ResponseBody + @RequestMapping("/purchaseOrder/deliveryMngAcceptancePartGridList.do") + public Map deliveryMngAcceptancePartGridList(HttpSession session, HttpServletRequest request, @RequestParam Map paramMap){ + commonService.selectListPagingNew("purchaseOrder.deliveryMngPartList", request, paramMap); + return paramMap; + } + /** * 입고 관리 */ diff --git a/src/com/pms/mapper/purchaseOrder.xml b/src/com/pms/mapper/purchaseOrder.xml index c580968..320d447 100644 --- a/src/com/pms/mapper/purchaseOrder.xml +++ b/src/com/pms/mapper/purchaseOrder.xml @@ -6156,4 +6156,165 @@ FROM( WHERE MULTI_MASTER_OBJID = #{OBJID} + + + \ No newline at end of file -- 2.49.1 From 79332f25a44935e77b5337fa3ba3f9e22e81b71c Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 19 Jan 2026 18:23:53 +0900 Subject: [PATCH 5/5] =?UTF-8?q?=ED=92=88=EC=9D=98=EC=84=9C=EB=8A=94=20?= =?UTF-8?q?=EA=B3=B5=EA=B8=89=EC=97=85=EC=B2=B4=EB=B3=84=20=EC=95=84?= =?UTF-8?q?=EB=8B=98;;?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../salesmgmt/service/SalesMngService.java | 105 +++++++----------- 1 file changed, 40 insertions(+), 65 deletions(-) diff --git a/src/com/pms/salesmgmt/service/SalesMngService.java b/src/com/pms/salesmgmt/service/SalesMngService.java index fe9ca0f..d0e8b59 100644 --- a/src/com/pms/salesmgmt/service/SalesMngService.java +++ b/src/com/pms/salesmgmt/service/SalesMngService.java @@ -2758,7 +2758,7 @@ public class SalesMngService { } /** - * 구매요청서 품의서 생성 (PURCHASE_REG 전용) - 업체별로 분리 생성 + * 구매요청서 품의서 생성 (PURCHASE_REG 전용) - 하나의 품의서로 생성 */ public Map createProposalFromPurchaseReg(HttpServletRequest request, Map paramMap) { SqlSession sqlSession = null; @@ -2794,68 +2794,46 @@ public class SalesMngService { return resultMap; } - // 3. 업체별로 품목 그룹화 - Map> partsByVendor = new LinkedHashMap>(); - for(Map part : targetParts) { - String vendorPm = CommonUtils.checkNull(part.get("VENDOR_PM")); - if(vendorPm.isEmpty()) { - vendorPm = CommonUtils.checkNull(part.get("vendor_pm")); - } - - if(!partsByVendor.containsKey(vendorPm)) { - partsByVendor.put(vendorPm, new ArrayList()); - } - partsByVendor.get(vendorPm).add(part); - } + // 3. 품의서 번호 생성 + String proposalNo = (String)sqlSession.selectOne("salesMng.getNextProposalNo"); - // 4. 업체별로 품의서 생성 - List createdProposalNos = new ArrayList(); + // 4. 품의서 마스터 생성 (하나만) + Map proposalMaster = new HashMap(); + String proposalMasterObjid = CommonUtils.createObjId(); + proposalMaster.put("OBJID", proposalMasterObjid); + proposalMaster.put("REQUEST_MNG_NO", proposalNo); + proposalMaster.put("PROJECT_NO", purchaseRequestInfo.get("PROJECT_NO")); + proposalMaster.put("PURCHASE_TYPE", purchaseRequestInfo.get("PURCHASE_TYPE")); + proposalMaster.put("ORDER_TYPE", purchaseRequestInfo.get("ORDER_TYPE")); + proposalMaster.put("PRODUCT_NAME", purchaseRequestInfo.get("PRODUCT_NAME")); + proposalMaster.put("AREA_CD", purchaseRequestInfo.get("AREA_CD")); + proposalMaster.put("CUSTOMER_OBJID", purchaseRequestInfo.get("CUSTOMER_OBJID")); + proposalMaster.put("PAID_TYPE", purchaseRequestInfo.get("PAID_TYPE")); + proposalMaster.put("DELIVERY_REQUEST_DATE", purchaseRequestInfo.get("DELIVERY_REQUEST_DATE")); + proposalMaster.put("REQUEST_USER_ID", userId); + proposalMaster.put("WRITER", userId); + + sqlSession.insert("salesMng.insertProposalFromPurchaseReg", proposalMaster); + + // 5. 모든 품목을 하나의 품의서에 생성 List allSourceObjids = new ArrayList(); - - for(String vendorPm : partsByVendor.keySet()) { - List vendorParts = partsByVendor.get(vendorPm); - - // 품의서 번호 생성 - String proposalNo = (String)sqlSession.selectOne("salesMng.getNextProposalNo"); - createdProposalNos.add(proposalNo); - - // 품의서 마스터 생성 - Map proposalMaster = new HashMap(); - String proposalMasterObjid = CommonUtils.createObjId(); - proposalMaster.put("OBJID", proposalMasterObjid); - proposalMaster.put("REQUEST_MNG_NO", proposalNo); - proposalMaster.put("PROJECT_NO", purchaseRequestInfo.get("PROJECT_NO")); - proposalMaster.put("PURCHASE_TYPE", purchaseRequestInfo.get("PURCHASE_TYPE")); - proposalMaster.put("ORDER_TYPE", purchaseRequestInfo.get("ORDER_TYPE")); - proposalMaster.put("PRODUCT_NAME", purchaseRequestInfo.get("PRODUCT_NAME")); - proposalMaster.put("AREA_CD", purchaseRequestInfo.get("AREA_CD")); - proposalMaster.put("CUSTOMER_OBJID", purchaseRequestInfo.get("CUSTOMER_OBJID")); - proposalMaster.put("PAID_TYPE", purchaseRequestInfo.get("PAID_TYPE")); - proposalMaster.put("DELIVERY_REQUEST_DATE", purchaseRequestInfo.get("DELIVERY_REQUEST_DATE")); - proposalMaster.put("REQUEST_USER_ID", userId); - proposalMaster.put("WRITER", userId); - - sqlSession.insert("salesMng.insertProposalFromPurchaseReg", proposalMaster); - - // 해당 업체의 품목 생성 - for(Map part : vendorParts) { - String sourceObjid = CommonUtils.checkNull(part.get("OBJID")); - if(sourceObjid.isEmpty()) { - sourceObjid = CommonUtils.checkNull(part.get("objid")); - } - allSourceObjids.add(sourceObjid); - - Map partParam = new HashMap(); - partParam.put("NEW_OBJID", CommonUtils.createObjId()); - partParam.put("PROPOSAL_MASTER_OBJID", proposalMasterObjid); - partParam.put("SOURCE_OBJID", sourceObjid); - partParam.put("WRITER", userId); - - sqlSession.insert("salesMng.insertProposalPartFromPurchaseReg", partParam); + for(Map part : targetParts) { + String sourceObjid = CommonUtils.checkNull(part.get("OBJID")); + if(sourceObjid.isEmpty()) { + sourceObjid = CommonUtils.checkNull(part.get("objid")); } + allSourceObjids.add(sourceObjid); + + Map partParam = new HashMap(); + partParam.put("NEW_OBJID", CommonUtils.createObjId()); + partParam.put("PROPOSAL_MASTER_OBJID", proposalMasterObjid); + partParam.put("SOURCE_OBJID", sourceObjid); + partParam.put("WRITER", userId); + + sqlSession.insert("salesMng.insertProposalPartFromPurchaseReg", partParam); } - // 5. 원본 품목 PROPOSAL_DATE 업데이트 + // 6. 원본 품목 PROPOSAL_DATE 업데이트 if(!allSourceObjids.isEmpty()) { Map updateParam = new HashMap(); updateParam.put("PART_OBJIDS", allSourceObjids); @@ -2865,13 +2843,10 @@ public class SalesMngService { sqlSession.commit(); // 결과 메시지 생성 - String message = "품의서가 " + createdProposalNos.size() + "건 생성되었습니다.\n"; - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < createdProposalNos.size(); i++) { - if(i > 0) sb.append(", "); - sb.append(createdProposalNos.get(i)); - } - message += sb.toString(); + String message = "품의서가 생성되었습니다. (" + proposalNo + ")"; + + List createdProposalNos = new ArrayList(); + createdProposalNos.add(proposalNo); resultMap.put("resultFlag", "S"); resultMap.put("message", message); -- 2.49.1