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/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/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/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..f42f22a --- /dev/null +++ b/WebContent/WEB-INF/view/salesMng/quotationRequestList.jsp @@ -0,0 +1,350 @@ +<%@ 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/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/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/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 b509da5..320d447 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') + + \ No newline at end of file diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index 9143a35..8cbe26c 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -721,7 +721,8 @@ VALUES COALESCE(NULLIF(SRM.AREA_CD, ''), SM.AREA_CD) AS AREA_CD, -- 국내/해외 COALESCE(NULLIF(SRM.CUSTOMER_OBJID, ''), SM.OBJID::VARCHAR) AS CUSTOMER_OBJID, -- 고객사 COALESCE(NULLIF(SRM.PAID_TYPE, ''), CM.PAID_TYPE) AS PAID_TYPE, -- 유/무상 - CM.CATEGORY_CD AS CATEGORY_CD -- 제품유형 코드 ID (드롭다운 선택용) + CM.CATEGORY_CD AS CATEGORY_CD, -- 제품유형 코드 ID (드롭다운 선택용) + SRM.DOC_TYPE -- 문서유형 (PURCHASE_REQUEST, PURCHASE_REG, PROPOSAL 등) -- 품번/품명: 첫 번째 품목 + 외 N건 형태 + SELECT 'P'||TO_CHAR(NOW(),'YYYYMMDD')||'-'||LPAD((COALESCE(MAX(SUBSTR(REQUEST_MNG_NO,11,3))::INTEGER, 0)+1)::TEXT,3,'0') FROM SALES_REQUEST_MASTER - WHERE DOC_TYPE = 'PROPOSAL' + WHERE DOC_TYPE IN ('PROPOSAL', 'PURCHASE_REG_PROPOSAL') AND REQUEST_MNG_NO LIKE 'P'||TO_CHAR(NOW(),'YYYYMMDD')||'%' @@ -3776,7 +3790,7 @@ ORDER BY V.PATH2 'create', #{WRITER}, NOW(), - 'PROPOSAL' + #{DOC_TYPE} ) @@ -4095,7 +4109,11 @@ ORDER BY V.PATH2 ) A ON SRM.OBJID::VARCHAR = A.TARGET_OBJID::VARCHAR WHERE 1=1 AND SRM.STATUS IN ('create', 'approvalRequest', 'approvalComplete', 'reject') - AND SRM.DOC_TYPE = '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}%' @@ -4125,6 +4143,130 @@ ORDER BY V.PATH2 ORDER BY SRM.REGDATE DESC + + + + 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 + + + + + UPDATE QUOTATION_REQUEST_MASTER + SET MAIL_SEND_YN = 'Y', + MAIL_SEND_DATE = NOW() + WHERE OBJID = #{QUOTATION_REQUEST_MASTER_OBJID}::NUMERIC + + + + + + + + + + + + + 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 9cc9064..52110b0 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; @@ -1336,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 @@ -1538,4 +1557,266 @@ 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; + } + + /** + * 견적요청서 메일 발송 팝업 + */ + @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); + } + + /** + * 구매요청서 품의서 대상 품목 조회 + */ + @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 a488fa4..d0e8b59 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; @@ -37,6 +38,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; /** @@ -1973,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(); @@ -1991,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); @@ -2137,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 생성 (대상 품목 복사) @@ -2194,4 +2210,657 @@ 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; + } + + /** + * 견적요청서 정보 조회 (메일 발송용) + */ + 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; + } + + /** + * 구매요청서 품의서 대상 품목 조회 + */ + 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. 품의서 번호 생성 + String proposalNo = (String)sqlSession.selectOne("salesMng.getNextProposalNo"); + + // 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(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); + } + + // 6. 원본 품목 PROPOSAL_DATE 업데이트 + if(!allSourceObjids.isEmpty()) { + Map updateParam = new HashMap(); + updateParam.put("PART_OBJIDS", allSourceObjids); + sqlSession.update("salesMng.updateProposalDateForPurchaseRegParts", updateParam); + } + + sqlSession.commit(); + + // 결과 메시지 생성 + String message = "품의서가 생성되었습니다. (" + proposalNo + ")"; + + List createdProposalNos = new ArrayList(); + createdProposalNos.add(proposalNo); + + 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);