품의서 개발 & DB에 저장된 메뉴명 표시

This commit is contained in:
2025-11-27 17:26:06 +09:00
parent 25ddce6480
commit ef9a6d2ac1
9 changed files with 722 additions and 121 deletions

View File

@@ -23,32 +23,9 @@ try {
sqlSession.close();
}
// 메뉴 이름 조회
// 메뉴 이름 조회 (공통 유틸 사용)
String menuObjId = request.getParameter("menuObjId");
if(menuObjId != null && !menuObjId.isEmpty()) {
org.apache.ibatis.session.SqlSession sqlSession = com.pms.common.SqlMapConfig.getInstance().getSqlSession();
java.util.Map<String, Object> menuParam = new java.util.HashMap<String, Object>();
menuParam.put("OBJID", menuObjId);
java.util.Map menuInfo = (java.util.Map)sqlSession.selectOne("admin.selectMenuInfo", menuParam);
if(menuInfo != null && menuInfo.get("MENU_NAME_KOR") != null) {
// 상위 메뉴 이름 조회
String parentObjId = CommonUtils.checkNull(menuInfo.get("PARENT_OBJ_ID"));
if(!parentObjId.isEmpty() && !"0".equals(parentObjId)) {
menuParam.put("OBJID", parentObjId);
java.util.Map parentMenuInfo = (java.util.Map)sqlSession.selectOne("admin.selectMenuInfo", menuParam);
if(parentMenuInfo != null && parentMenuInfo.get("MENU_NAME_KOR") != null) {
menuName = CommonUtils.checkNull(parentMenuInfo.get("MENU_NAME_KOR")) + "_" + CommonUtils.checkNull(menuInfo.get("MENU_NAME_KOR"));
} else {
menuName = CommonUtils.checkNull(menuInfo.get("MENU_NAME_KOR"));
}
} else {
menuName = CommonUtils.checkNull(menuInfo.get("MENU_NAME_KOR"));
}
}
sqlSession.close();
}
menuName = CommonUtils.getMenuName(menuObjId, menuName);
} catch(Exception e) {
e.printStackTrace();
}

View File

@@ -4,12 +4,25 @@
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<c:set var="now" value="<%=new java.util.Date() %>"/>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<%
// DB에서 메뉴명 조회 (공통 유틸 사용)
String menuObjId = request.getParameter("menuObjId");
String menuName = CommonUtils.getMenuName(menuObjId, "생산관리_M-BOM관리");
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
overflow-x: hidden;
width: 100%;
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript" src="/js/tabulator/tabulator_custom.js"></script>
<style>
/* 체크박스 컬럼 오른쪽에 얇은 구분선 추가 (데이터 행만) */
@@ -611,7 +624,7 @@ function fn_openPurchaseListPopup() {
<div class="content-box-s">
<div class="plm_menu_name_gdnsi">
<h2>
<span>생산관리_M-BOM관리</span>
<span><%=menuName%></span>
</h2>
<div class="btnArea">
<input type="button" class="plm_btns" value="조회" id="btnSearch">

View File

@@ -6,12 +6,23 @@
<%@include file= "/init.jsp" %>
<c:set var="now" value="<%=new java.util.Date() %>"/>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<%
// DB에서 메뉴명 조회 (공통 유틸 사용)
String menuObjId = request.getParameter("menuObjId");
String menuName = CommonUtils.getMenuName(menuObjId, "기본메뉴명");
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
overflow-x: hidden;
width: 100%;
margin: 0;
padding: 0;
}
.pmsPopupForm tr:last-child td {
border-bottom: none;
}
@@ -50,6 +61,15 @@
var _tabulGrid;
$(document).ready(function(){
// DOM 렌더링 완료 후 그리드 높이 계산
setTimeout(function() {
fnc_calculateContentHeight("gridDiv", 10);
}, 100);
$(window).resize(function() {
fnc_calculateContentHeight("gridDiv", 10);
});
$('.select2').select2();
$("input[type=text]").keyup(function(e){
@@ -125,14 +145,11 @@ $(document).ready(function(){
var columns = [
{title:'OBJID', field:'OBJID', visible: false},
{title:'STATUS', field:'STATUS', visible: false},
{headerHozAlign:'center', hozAlign:'left', title:"품의서 No", field:"PROPOSAL_NO", widthGrow:1.2, frozen:true,
cellClick: function(e, cell){
var objId = cell.getData().OBJID;
fn_openProposalFormPopUp(objId);
},
formatter: function(cell){
return '<a href="#" style="color:#0066cc; text-decoration:underline;">' + fnc_checkNull(cell.getValue()) + '</a>';
}
{headerHozAlign:'center', hozAlign:'center', title:"품의서 No", field:"PROPOSAL_NO", widthGrow:1.2, frozen:true,
formatter: fnc_createGridAnchorTag,
cellClick : function(e, cell) {
fn_openProposalFormPopUp(cell.getData().OBJID);
}
},
{headerHozAlign:'center', hozAlign:'left', title:"프로젝트번호", field:"PROJECT_NUMBER", widthGrow:1.3},
{headerHozAlign:'center', hozAlign:'center', title:"구매유형", field:"PURCHASE_TYPE_NAME", widthGrow:1.0},
@@ -175,15 +192,15 @@ function fn_openProposalFormPopUp(objId){
<div class="content-box-s">
<div class="plm_menu_name_gdnsi">
<h2>
<span>구매관리_품의서관리</span>
<span><%=menuName%></span>
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval" style="background:#17a2b8; color:white;">
<input type="button" value="발주서생성" class="plm_btns" id="btnCreatePO" style="background:#28a745; color:white;">
<%-- <input type="button" value="초기화" class="plm_btns" id="btnReset">
<input type="button" value="엑셀 다운로드" class="plm_btns" id="btnExcel"> --%>
</div>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval" style="background:#17a2b8; color:white;">
<input type="button" value="발주서생성" class="plm_btns" id="btnCreatePO" style="background:#28a745; color:white;">
<%-- <input type="button" value="초기화" class="plm_btns" id="btnReset">
<input type="button" value="엑셀 다운로드" class="plm_btns" id="btnExcel"> --%>
</div>
</div>
<div id="plmSearchZon">
<table>

View File

@@ -722,15 +722,10 @@ function fn_save() {
}
// 품의서작성일 자동 설정 (현재 날짜)
var today = new Date();
var proposalDate = today.getFullYear() + '-' +
String(today.getMonth() + 1).padStart(2, '0') + '-' +
String(today.getDate()).padStart(2, '0');
gridData.forEach(function(item) {
if(!item.PROPOSAL_DATE) {
item.PROPOSAL_DATE = proposalDate;
}
// PROPOSAL_DATE는 품의서 생성 시에만 자동 설정되므로 여기서는 제거
// (기존에 저장된 값이 있으면 유지, 없으면 NULL로 유지)
// 사용여부 변환: 사용/미사용 → Y/N
if(item.USE_YN === '사용') {
item.USE_YN = 'Y';

View File

@@ -5,6 +5,11 @@
<%@include file= "/init.jsp" %>
<c:set var="now" value="<%=new java.util.Date() %>"/>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<%
// DB에서 메뉴명 조회 (공통 유틸 사용)
String menuObjId = request.getParameter("menuObjId");
String menuName = CommonUtils.getMenuName(menuObjId, "기본메뉴명");
%>
<!DOCTYPE html>
<html>
<head>
@@ -103,57 +108,10 @@ $(document).ready(function(){
fn_openSalesRequestFormPopUp("");
});
//품의서 생성 (TODO: 품의서 생성 기능 구현 필요 - 기존 발주서 작성 기능과 다름)
/*
// 품의서 생성
$("#btnReg").click(function(){
var selectedRowIds = _tabulGrid.getSelectedData();
console.log(selectedRowIds);
if(selectedRowIds.length==0){
Swal.fire("품의서 생성할 행을 선택해주십시오.");
return false;
}
var targetStatus = fnc_checkNull(selectedRowIds[0].STATUS_TITLE);
if(targetStatus != "접수"){
Swal.fire("접수 상태일 경우 품의서 생성이 가능합니다.");
return false;
}
//var sales_request_objId = fnc_checkNull(selectedRowIds[0].OBJID);
//fn_formPopUp("",sales_request_objId);
Swal.fire({
title: '품의서를 생성 하시겠습니까?',
text: '',
icon: 'warning',
showCancelButton: true, // cancel버튼 보이기. 기본은 원래 없음
confirmButtonColor: '#3085d6', // confrim 버튼 색깔 지정
cancelButtonColor: '#d33', // cancel 버튼 색깔 지정
confirmButtonText: '확인', // confirm 버튼 텍스트 지정
cancelButtonText: '취소', // cancel 버튼 텍스트 지정
reverseButtons: false, // 버튼 순서 거꾸로
}).then(result => {
// 만약 Promise리턴을 받으면,
if (result.isConfirmed) { // 만약 모달창에서 confirm 버튼을 눌렀다면
$.ajax({
url:"/purchaseOrder/purchaseOrder_salesRequest_Save.do"
,type:"POST"
,data: $("#form1").serialize() + "&jqGrid="+ encodeURIComponent(JSON.stringify(selectedRowIds))
,dataType:"json"
,success:function(data){
if(data =="SUCCESS"){
alert("저장되었습니다.");
};
fn_search();
}
,error: function(jqxhr, status, error){
}
});
}
});
fn_createProposal();
});
*/
$("#btnOrderBOMReg").click(function(){
fn_salesRequestTargetBOMListPopUp();
@@ -478,6 +436,139 @@ function fn_formPopUp(objId,sales_request_objid){
hiddenForm.target = target;
hiddenForm.submit(); */
}
/**
* 품의서 생성 함수
* - 선택된 구매요청서에서 단가가 입력된 품목만 필터링
* - 이미 품의서가 생성된 품목은 제외
* - 하나의 품의서로 생성
*/
function fn_createProposal() {
// 1. 선택된 행 확인
var selectedRows = _tabulGrid.getSelectedData();
if(selectedRows.length == 0) {
Swal.fire({
title: '알림',
text: '품의서를 생성할 구매요청서를 선택해주세요.',
icon: 'info'
});
return;
}
if(selectedRows.length > 1) {
Swal.fire({
title: '알림',
text: '한 번에 하나의 구매요청서만 선택해주세요.',
icon: 'info'
});
return;
}
var selectedRow = selectedRows[0];
var salesRequestObjid = fnc_checkNull(selectedRow.OBJID);
// 2. 구매요청서의 품목 중 단가가 입력되고 품의서 미생성된 품목 조회
$.ajax({
url: "/salesMng/getProposalTargetParts.do",
type: "POST",
data: {
SALES_REQUEST_MASTER_OBJID: salesRequestObjid
},
dataType: "json",
success: function(response) {
if(response.resultFlag === "S") {
var targetParts = response.data;
// 3. 대상 품목 확인
if(!targetParts || targetParts.length == 0) {
Swal.fire({
title: '알림',
text: '품의서를 생성할 품목이 없습니다.\n(단가가 입력되고 품의서가 생성되지 않은 품목만 대상)',
icon: 'info'
});
return;
}
// 4. 품의서 생성 확인
var partCount = targetParts.length;
var partList = targetParts.map(function(part) {
return '- ' + fnc_checkNull(part.PART_NO) + ' / ' + fnc_checkNull(part.PART_NAME);
}).join('\n');
Swal.fire({
title: '품의서 생성',
html: '<div style="text-align:left;">' +
'<p>총 <strong>' + partCount + '건</strong>의 품목으로 품의서를 생성합니다.</p>' +
'<div style="max-height:200px; overflow-y:auto; border:1px solid #ddd; padding:10px; margin-top:10px; font-size:12px;">' +
partList +
'</div>' +
'</div>',
icon: 'question',
showCancelButton: true,
confirmButtonText: '생성',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
fn_executeCreateProposal(salesRequestObjid, targetParts);
}
});
} else {
Swal.fire({
title: '오류',
text: response.message || '품목 조회 중 오류가 발생했습니다.',
icon: 'error'
});
}
},
error: function(xhr, status, error) {
Swal.fire({
title: '오류',
text: '서버 통신 중 오류가 발생했습니다.22',
icon: 'error'
});
}
});
}
/**
* 품의서 생성 실행
*/
function fn_executeCreateProposal(salesRequestObjid, targetParts) {
$.ajax({
url: "/salesMng/createProposalFromPurchaseList.do",
type: "POST",
data: {
SALES_REQUEST_MASTER_OBJID: salesRequestObjid,
TARGET_PARTS: JSON.stringify(targetParts)
},
dataType: "json",
success: function(response) {
if(response.resultFlag === "S") {
Swal.fire({
title: '생성 완료',
text: '품의서가 생성되었습니다.\n품의서 관리에서 확인하세요.',
icon: 'success'
}).then(() => {
fn_search(); // 목록 새로고침
});
} else {
Swal.fire({
title: '오류',
text: response.message || '품의서 생성 중 오류가 발생했습니다.',
icon: 'error'
});
}
},
error: function(xhr, status, error) {
Swal.fire({
title: '오류',
text: '서버 통신 중 오류가 발생했습니다.',
icon: 'error'
});
}
});
}
</script>
</head>
<body class="backcolor">
@@ -491,7 +582,7 @@ function fn_formPopUp(objId,sales_request_objid){
<div class="content-box-s">
<div class="plm_menu_name_gdnsi">
<h2>
<span>구매관리_구매리스트관리</span>
<span><%=menuName%></span>
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch">

View File

@@ -1917,6 +1917,76 @@ public class CommonUtils {
return concatenatedValues;
}
/**
* menuObjId로 메뉴명을 조회한다 (상위메뉴_하위메뉴 형식)
*
* @param menuObjId 메뉴 OBJID
* @param defaultMenuName 기본 메뉴명 (조회 실패 시 반환)
* @return 메뉴명 (상위메뉴_하위메뉴 형식)
*/
public static String getMenuName(String menuObjId, String defaultMenuName) {
String menuName = defaultMenuName;
org.apache.ibatis.session.SqlSession sqlSession = null;
try {
if(menuObjId == null || menuObjId.isEmpty()) {
logger.debug("menuObjId가 null 또는 빈 문자열입니다. 기본값 반환: " + defaultMenuName);
return menuName;
}
sqlSession = com.pms.common.SqlMapConfig.getInstance().getSqlSession();
Map<String, Object> menuParam = new HashMap<String, Object>();
menuParam.put("OBJID", menuObjId);
// MyBatis resultType="map"은 컬럼명을 소문자로 반환하므로 대문자로 변환 필요
Map menuInfo = (Map)sqlSession.selectOne("admin.selectMenuInfo", menuParam);
if(menuInfo != null) {
// Map의 key를 대문자로 변환
menuInfo = toUpperCaseMapKey(menuInfo);
logger.debug("메뉴 정보 조회 성공: " + menuInfo);
if(menuInfo.get("MENU_NAME_KOR") != null) {
// 상위 메뉴 이름 조회
String parentObjId = checkNull(menuInfo.get("PARENT_OBJ_ID"));
if(!parentObjId.isEmpty() && !"0".equals(parentObjId)) {
menuParam.put("OBJID", parentObjId);
Map parentMenuInfo = (Map)sqlSession.selectOne("admin.selectMenuInfo", menuParam);
if(parentMenuInfo != null) {
// 상위 메뉴도 대문자로 변환
parentMenuInfo = toUpperCaseMapKey(parentMenuInfo);
if(parentMenuInfo.get("MENU_NAME_KOR") != null) {
menuName = checkNull(parentMenuInfo.get("MENU_NAME_KOR")) + "_" + checkNull(menuInfo.get("MENU_NAME_KOR"));
} else {
menuName = checkNull(menuInfo.get("MENU_NAME_KOR"));
}
} else {
menuName = checkNull(menuInfo.get("MENU_NAME_KOR"));
}
} else {
menuName = checkNull(menuInfo.get("MENU_NAME_KOR"));
}
logger.debug("최종 메뉴명: " + menuName);
} else {
logger.warn("MENU_NAME_KOR이 null입니다. menuObjId: " + menuObjId);
}
} else {
logger.warn("메뉴 정보를 찾을 수 없습니다. menuObjId: " + menuObjId);
}
} catch(Exception e) {
logger.error("메뉴명 조회 중 오류 발생 (menuObjId: " + menuObjId + "): " + e.getMessage(), e);
} finally {
if(sqlSession != null) {
try {
sqlSession.close();
} catch(Exception e) {
logger.error("SqlSession 종료 중 오류 발생: " + e.getMessage(), e);
}
}
}
return menuName;
}
}

View File

@@ -607,12 +607,13 @@
PRODUCT_NAME,
AREA_CD,
CUSTOMER_OBJID,
PAID_TYPE
PAID_TYPE,
DOC_TYPE
)
VALUES
(
#{SALES_REQUEST_MASTER_OBJID },
(SELECT 'R'||TO_CHAR(NOW(),'YYYYMMDD')||'-'||LPAD((COALESCE(MAX(SUBSTR(REQUEST_MNG_NO,11,13)),'0')::INTEGER+1)::TEXT,3,'0') FROM SALES_REQUEST_MASTER),
(SELECT 'R'||TO_CHAR(NOW(),'YYYYMMDD')||'-'||LPAD((COALESCE(MAX(SUBSTR(REQUEST_MNG_NO,11,13)),'0')::INTEGER+1)::TEXT,3,'0') FROM SALES_REQUEST_MASTER WHERE DOC_TYPE = 'PURCHASE_REQUEST' OR DOC_TYPE IS NULL),
#{REQUEST_CD },
#{PROJECT_NO },
#{RELEASE },
@@ -631,7 +632,8 @@ VALUES
#{PRODUCT_NAME },
#{AREA_CD },
#{CUSTOMER_OBJID },
#{PAID_TYPE }
#{PAID_TYPE },
'PURCHASE_REQUEST'
) ON CONFLICT (OBJID) DO
UPDATE
SET
@@ -1150,6 +1152,8 @@ VALUES
) SRP ON SRP.SALES_REQUEST_MASTER_OBJID::VARCHAR = SRM.OBJID::VARCHAR
AND SRP.SUB_RNUM = 1 -->
WHERE 1=1
-- 구매요청서만 조회 (품의서 제외)
AND (SRM.DOC_TYPE = 'PURCHASE_REQUEST' OR SRM.DOC_TYPE IS NULL)
<if test="Year !=null and Year != '' ">
AND TO_CHAR(REGDATE,'YYYY') = #{Year}
</if>
@@ -3292,7 +3296,7 @@ ORDER BY V.PATH2
VENDOR = #{VENDOR_PM},
UNIT_PRICE = COALESCE(NULLIF(TRIM(#{UNIT_PRICE}::TEXT), '')::NUMERIC, 0),
TOTAL_PRICE = COALESCE(NULLIF(TRIM(#{TOTAL_PRICE}::TEXT), '')::NUMERIC, 0),
PROPOSAL_DATE = CASE WHEN NULLIF(TRIM(#{PROPOSAL_DATE}::TEXT), '') IS NOT NULL THEN #{PROPOSAL_DATE}::DATE ELSE NULL END,
-- PROPOSAL_DATE는 품의서 생성 시에만 자동 설정 (여기서는 업데이트하지 않음)
EDITER = #{EDITER},
EDIT_DATE = NOW()
WHERE OBJID::VARCHAR = #{OBJID}
@@ -3307,7 +3311,7 @@ ORDER BY V.PATH2
VENDOR_PM = #{VENDOR_PM},
UNIT_PRICE = COALESCE(NULLIF(TRIM(#{UNIT_PRICE}::TEXT), '')::NUMERIC, 0),
TOTAL_PRICE = COALESCE(NULLIF(TRIM(#{TOTAL_PRICE}::TEXT), '')::NUMERIC, 0),
PROPOSAL_DATE = CASE WHEN NULLIF(TRIM(#{PROPOSAL_DATE}::TEXT), '') IS NOT NULL THEN #{PROPOSAL_DATE}::DATE ELSE NULL END,
-- PROPOSAL_DATE는 품의서 생성 시에만 자동 설정 (여기서는 업데이트하지 않음)
WRITER = #{EDITER}
WHERE OBJID::VARCHAR = #{OBJID}
</update>
@@ -3327,10 +3331,18 @@ ORDER BY V.PATH2
ORDER BY REGDATE DESC
</select>
<!-- 다음 요청번호 생성 (R + YYYYMMDD + - + 3자리 순번) -->
<!-- 다음 요청번호 생성 (R + YYYYMMDD + - + 3자리 순번) - 구매요청서용 -->
<select id="getNextRequestMngNo" resultType="string">
SELECT 'R'||TO_CHAR(NOW(),'YYYYMMDD')||'-'||LPAD((COALESCE(MAX(SUBSTR(REQUEST_MNG_NO,11,13)),'0')::INTEGER+1)::TEXT,3,'0')
FROM SALES_REQUEST_MASTER
WHERE DOC_TYPE = 'PURCHASE_REQUEST' OR DOC_TYPE IS NULL
</select>
<!-- 다음 품의서 번호 생성 (P + YYYYMMDD + - + 3자리 순번) - 품의서용 -->
<select id="getNextProposalNo" resultType="string">
SELECT 'P'||TO_CHAR(NOW(),'YYYYMMDD')||'-'||LPAD((COALESCE(MAX(SUBSTR(REQUEST_MNG_NO,11,13)),'0')::INTEGER+1)::TEXT,3,'0')
FROM SALES_REQUEST_MASTER
WHERE DOC_TYPE = 'PROPOSAL'
</select>
<!-- M-BOM에서 구매리스트 생성 - SALES_REQUEST_MASTER만 생성 (SALES_REQUEST_PART는 생성 안 함) -->
@@ -3343,7 +3355,8 @@ ORDER BY V.PATH2
REQUEST_USER_ID,
STATUS,
WRITER,
REGDATE
REGDATE,
DOC_TYPE
) VALUES (
#{OBJID},
#{REQUEST_MNG_NO},
@@ -3352,13 +3365,197 @@ ORDER BY V.PATH2
#{REQUEST_USER_ID},
#{STATUS},
#{WRITER},
NOW()
NOW(),
'PURCHASE_REQUEST'
)
</insert>
<!-- ==================== 품의서 관리 ==================== -->
<!-- 품의서 대상 품목 조회 - M-BOM 기반 (MBOM_DETAIL에서 조회) -->
<select id="getProposalTargetPartsFromMBom" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
MD.OBJID,
MD.PART_OBJID,
PM.PART_NO,
PM.PART_NAME,
MD.QTY,
MD.UNIT_PRICE,
MD.TOTAL_PRICE,
MD.VENDOR AS VENDOR_PM,
(SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID::VARCHAR = MD.VENDOR) AS VENDOR_NAME,
MD.NET_QTY,
MD.PO_QTY,
'MBOM' AS DATA_SOURCE
FROM
MBOM_DETAIL MD
LEFT JOIN PART_MNG PM ON MD.PART_OBJID::VARCHAR = PM.OBJID::VARCHAR
WHERE
MD.MBOM_HEADER_OBJID = #{MBOM_HEADER_OBJID}
-- 단가가 입력되어 있고
AND MD.UNIT_PRICE IS NOT NULL
AND MD.UNIT_PRICE > 0
-- 품의서가 생성되지 않은 품목만
AND MD.PROPOSAL_DATE IS NULL
ORDER BY MD.REGDATE
</select>
<!-- 품의서 대상 품목 조회 - 수동 작성 (SALES_REQUEST_PART에서 조회) -->
<select id="getProposalTargetPartsFromManual" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
SRP.OBJID,
SRP.PART_OBJID,
PM.PART_NO,
PM.PART_NAME,
SRP.QTY,
SRP.UNIT_PRICE,
SRP.TOTAL_PRICE,
SRP.VENDOR_PM,
(SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID::VARCHAR = SRP.VENDOR_PM) AS VENDOR_NAME,
SRP.NET_QTY,
SRP.PO_QTY,
'MANUAL' AS DATA_SOURCE
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}
-- 단가가 입력되어 있고
AND SRP.UNIT_PRICE IS NOT NULL
AND SRP.UNIT_PRICE > 0
-- 품의서가 생성되지 않은 품목만
AND SRP.PROPOSAL_DATE IS NULL
ORDER BY SRP.REGDATE
</select>
<!-- 품의서 생성 (SALES_REQUEST_MASTER에 INSERT) -->
<insert id="insertProposal" parameterType="map">
INSERT INTO SALES_REQUEST_MASTER (
OBJID,
REQUEST_MNG_NO,
PROJECT_NO,
MBOM_HEADER_OBJID,
PURCHASE_TYPE,
ORDER_TYPE,
PRODUCT_NAME,
AREA_CD,
CUSTOMER_OBJID,
PAID_TYPE,
REQUEST_USER_ID,
STATUS,
WRITER,
REGDATE,
DOC_TYPE
) VALUES (
#{OBJID},
#{REQUEST_MNG_NO},
#{PROJECT_NO},
#{MBOM_HEADER_OBJID},
#{PURCHASE_TYPE},
#{ORDER_TYPE},
#{PRODUCT_NAME},
#{AREA_CD},
#{CUSTOMER_OBJID},
#{PAID_TYPE},
#{REQUEST_USER_ID},
'create',
#{WRITER},
NOW(),
'PROPOSAL'
)
</insert>
<!-- 품의서용 SALES_REQUEST_PART 생성 (M-BOM 기반 품목 복사) -->
<insert id="insertProposalPartFromMBom" parameterType="map">
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,
UNIT_PRICE,
TOTAL_PRICE,
VENDOR AS VENDOR_PM,
NET_QTY,
PO_QTY,
USE_YN,
NOW() AS PROPOSAL_DATE,
#{WRITER},
NOW()
FROM MBOM_DETAIL
WHERE OBJID = #{SOURCE_OBJID}
</insert>
<!-- 품의서용 SALES_REQUEST_PART 생성 (수동 작성 품목 복사) -->
<insert id="insertProposalPartFromManual" parameterType="map">
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,
UNIT_PRICE,
TOTAL_PRICE,
VENDOR_PM,
NET_QTY,
PO_QTY,
USE_YN,
NOW() AS PROPOSAL_DATE,
#{WRITER},
NOW()
FROM SALES_REQUEST_PART
WHERE OBJID = #{SOURCE_OBJID}
</insert>
<!-- 품의서 생성 후 PROPOSAL_DATE 업데이트 (수동 작성 - SALES_REQUEST_PART) -->
<update id="updateProposalDateForManualParts" parameterType="map">
UPDATE SALES_REQUEST_PART
SET PROPOSAL_DATE = NOW()
WHERE OBJID IN
<foreach collection="PART_OBJIDS" item="objid" open="(" separator="," close=")">
#{objid}
</foreach>
</update>
<!-- 품의서 생성 후 PROPOSAL_DATE 업데이트 (M-BOM - MBOM_DETAIL) -->
<update id="updateProposalDateForMBomParts" parameterType="map">
UPDATE MBOM_DETAIL
SET PROPOSAL_DATE = NOW()
WHERE OBJID IN
<foreach collection="PART_OBJIDS" item="objid" open="(" separator="," close=")">
#{objid}
</foreach>
</update>
<!-- 품의서 목록 조회 -->
<select id="getProposalMngGridList" parameterType="map" resultType="map">
SELECT
@@ -3374,14 +3571,14 @@ ORDER BY V.PATH2
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = SRM.ORDER_TYPE) AS ORDER_TYPE_NAME,
SRM.PRODUCT_NAME,
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = SRM.PRODUCT_NAME) AS PRODUCT_NAME_TITLE,
-- 품번/품명 ("외 N건" 형태로 표시)
-- 품번/품명 ("외 N건" 형태로 표시) - 품의서는 항상 SALES_REQUEST_PART에 데이터가 있음
(
SELECT
CASE
WHEN COUNT(*) > 1 THEN
(SELECT PM.PART_NO FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)) || ' 외 ' || (COUNT(*) - 1) || '건'
COALESCE((SELECT PM.PART_NO FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)), '') || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
ELSE
(SELECT PM.PART_NO FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1))
COALESCE((SELECT PM.PART_NO FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)), '')
END
FROM SALES_REQUEST_PART
WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID
@@ -3390,9 +3587,9 @@ ORDER BY V.PATH2
SELECT
CASE
WHEN COUNT(*) > 1 THEN
(SELECT PM.PART_NAME FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)) || ' 외 ' || (COUNT(*) - 1) || '건'
COALESCE((SELECT PM.PART_NAME FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)), '') || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
ELSE
(SELECT PM.PART_NAME FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1))
COALESCE((SELECT PM.PART_NAME FROM PART_MNG PM WHERE PM.OBJID::VARCHAR = (SELECT PART_OBJID FROM SALES_REQUEST_PART WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)), '')
END
FROM SALES_REQUEST_PART
WHERE SALES_REQUEST_MASTER_OBJID = SRM.OBJID
@@ -3402,19 +3599,19 @@ ORDER BY V.PATH2
SELECT
CASE
WHEN COUNT(DISTINCT POM.PARTNER_OBJID) > 1 THEN
(SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID = (SELECT PARTNER_OBJID FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)::NUMERIC) || ' 외 ' || (COUNT(DISTINCT POM.PARTNER_OBJID) - 1) || '건'
COALESCE((SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID = (SELECT PARTNER_OBJID FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)::NUMERIC), '') || ' 외 ' || (COUNT(DISTINCT POM.PARTNER_OBJID) - 1)::TEXT || '건'
ELSE
(SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID = (SELECT PARTNER_OBJID FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)::NUMERIC)
COALESCE((SELECT SUPPLY_NAME FROM ADMIN_SUPPLY_MNG WHERE OBJID = (SELECT PARTNER_OBJID FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID ORDER BY REGDATE LIMIT 1)::NUMERIC), '')
END
FROM PURCHASE_ORDER_MASTER POM
WHERE POM.SALES_REQUEST_OBJID = SRM.OBJID
) AS SUPPLIER_NAME,
-- 총액
(SELECT SUM(COALESCE(TOTAL_PRICE, 0)) FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID) AS TOTAL_AMOUNT,
(SELECT SUM(COALESCE(TOTAL_PRICE::numeric, 0)) FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID) AS TOTAL_AMOUNT,
-- 메일발송 여부
(SELECT CASE WHEN COUNT(*) > 0 THEN 'Y' ELSE 'N' END FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID AND MAIL_SENT = 'Y') AS MAIL_SENT,
-- 발주일
(SELECT TO_CHAR(MAX(ORDER_DATE), 'YYYY-MM-DD') FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID) AS ORDER_DATE,
-- (SELECT CASE WHEN COUNT(*) > 0 THEN 'Y' ELSE 'N' END FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID AND MAIL_SENT = 'Y') AS MAIL_SENT,
-- 발주일 (일단 주석처리)
-- (SELECT TO_CHAR(MAX(ORDER_DATE), 'YYYY-MM-DD') FROM PURCHASE_ORDER_MASTER WHERE SALES_REQUEST_OBJID = SRM.OBJID) AS ORDER_DATE,
SRM.STATUS,
CASE
WHEN A.APPR_STATUS IS NOT NULL THEN A.APPR_STATUS
@@ -3468,7 +3665,7 @@ 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'
<if test="SEARCH_PROPOSAL_NO != null and !''.equals(SEARCH_PROPOSAL_NO)">
AND SRM.REQUEST_MNG_NO LIKE '%${SEARCH_PROPOSAL_NO}%'
</if>

View File

@@ -17,6 +17,7 @@ import java.util.Map;
import javax.servlet.http.HttpServletRequest;
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.web.bind.annotation.RequestMapping;
@@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.pms.common.JsonUtil;
import com.pms.common.SqlMapConfig;
import com.pms.common.bean.PersonBean;
import com.pms.common.utils.CommonUtils;
import com.pms.common.utils.Constants;
@@ -1300,4 +1302,90 @@ public class SalesMngController {
request.setAttribute("code_map", code_map);
return "/salesMng/proposalFormPopUp";
}
/**
* 품의서 생성 (JSP에서 호출)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/salesMng/createProposal.do")
public Map createProposal(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
resultMap = salesMngService.createProposal(request, paramMap);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("resultFlag", "F");
resultMap.put("message", "품의서 생성 중 오류가 발생했습니다: " + e.getMessage());
}
return resultMap;
}
/**
* 품의서 대상 품목 조회 (JSP에서 호출)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/salesMng/getProposalTargetParts.do")
public Map getProposalTargetParts(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
String salesRequestMasterObjid = CommonUtils.checkNull(paramMap.get("SALES_REQUEST_MASTER_OBJID"));
// Service의 공통 메서드 사용
Map partsInfo = salesMngService.getProposalTargetPartsWithInfo(sqlSession, salesRequestMasterObjid);
List<Map> targetParts = (List<Map>)partsInfo.get("targetParts");
if(targetParts != null && !targetParts.isEmpty()) {
resultMap.put("resultFlag", "S");
resultMap.put("data", targetParts);
} else {
resultMap.put("resultFlag", "F");
resultMap.put("message", "품의서 생성 대상 품목이 없습니다.\n(단가가 입력되고 품의서가 생성되지 않은 품목만 가능)");
}
} catch (Exception e) {
e.printStackTrace();
resultMap.put("resultFlag", "F");
resultMap.put("message", "품목 조회 중 오류가 발생했습니다: " + e.getMessage());
} finally {
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
/**
* 품의서 대상 품목 미리보기 (JSP에서 호출 - 별칭)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/salesMng/getProposalTargetPartsForPreview.do")
public Map getProposalTargetPartsForPreview(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
return getProposalTargetParts(request, paramMap);
}
/**
* 품의서 생성 실행 (JSP에서 호출)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/salesMng/createProposalFromPurchaseList.do")
public Map createProposalFromPurchaseList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
return createProposal(request, paramMap);
}
}

View File

@@ -1905,4 +1905,157 @@ public class SalesMngService {
return resultMap;
}
/**
* 품의서 대상 품목 조회 (공통 메서드)
* @param sqlSession
* @param salesRequestMasterObjid
* @return Map { targetParts: List<Map>, mbomHeaderObjid: String, purchaseRequestInfo: Map }
* @throws Exception
*/
public Map getProposalTargetPartsWithInfo(SqlSession sqlSession, String salesRequestMasterObjid) throws Exception {
Map result = new HashMap();
// 1. 구매요청서 정보 조회
Map paramMap = new HashMap();
paramMap.put("SALES_REQUEST_MASTER_OBJID", salesRequestMasterObjid);
Map purchaseRequestInfo = (Map)sqlSession.selectOne("salesMng.getSalesRequestMasterInfo", paramMap);
purchaseRequestInfo = CommonUtils.toUpperCaseMapKey(purchaseRequestInfo);
String mbomHeaderObjid = CommonUtils.checkNull(purchaseRequestInfo.get("MBOM_HEADER_OBJID"));
// 2. 품의서 대상 품목 조회 (M-BOM 기반 여부에 따라 분기)
List<Map> targetParts = null;
if(!mbomHeaderObjid.isEmpty()) {
// M-BOM 기반 -> MBOM_DETAIL에서 조회
Map mbomParam = new HashMap();
mbomParam.put("MBOM_HEADER_OBJID", mbomHeaderObjid);
targetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromMBom", mbomParam);
} else {
// 수동 작성 -> SALES_REQUEST_PART에서 조회
targetParts = sqlSession.selectList("salesMng.getProposalTargetPartsFromManual", paramMap);
}
targetParts = CommonUtils.keyChangeUpperList(targetParts);
// 3. 결과 반환
result.put("targetParts", targetParts);
result.put("mbomHeaderObjid", mbomHeaderObjid);
result.put("purchaseRequestInfo", purchaseRequestInfo);
return result;
}
/**
* 품의서 생성
* @param request
* @param paramMap
* @return
* @throws Exception
*/
public Map createProposal(HttpServletRequest request, Map<String, Object> paramMap) throws Exception {
Map resultMap = new HashMap();
SqlSession sqlSession = null;
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 partsInfo = getProposalTargetPartsWithInfo(sqlSession, salesRequestMasterObjid);
List<Map> targetParts = (List<Map>)partsInfo.get("targetParts");
String mbomHeaderObjid = (String)partsInfo.get("mbomHeaderObjid");
Map purchaseRequestInfo = (Map)partsInfo.get("purchaseRequestInfo");
if(targetParts == null || targetParts.isEmpty()) {
resultMap.put("resultFlag", "F");
resultMap.put("message", "품의서 생성 대상 품목이 없습니다. (단가가 입력되고 품의서가 생성되지 않은 품목만 가능)");
return resultMap;
}
// 2. 품의서 번호 생성
String proposalNo = (String)sqlSession.selectOne("salesMng.getNextProposalNo");
// 3. 품의서 마스터 생성
Map proposalMaster = new HashMap();
proposalMaster.put("OBJID", CommonUtils.createObjId());
proposalMaster.put("REQUEST_MNG_NO", proposalNo);
proposalMaster.put("PROJECT_NO", purchaseRequestInfo.get("PROJECT_NO"));
proposalMaster.put("MBOM_HEADER_OBJID", purchaseRequestInfo.get("MBOM_HEADER_OBJID"));
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("REQUEST_USER_ID", userId);
proposalMaster.put("WRITER", userId);
sqlSession.insert("salesMng.insertProposal", proposalMaster);
// 4. 품의서용 SALES_REQUEST_PART 생성 (대상 품목 복사)
String proposalMasterObjid = (String)proposalMaster.get("OBJID");
List<String> sourceObjids = new ArrayList<String>();
for(Map part : targetParts) {
String sourceObjid = CommonUtils.checkNull(part.get("OBJID"));
sourceObjids.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);
// M-BOM 기반 여부에 따라 INSERT 쿼리 분기
if(!mbomHeaderObjid.isEmpty()) {
// M-BOM 기반 -> MBOM_DETAIL에서 복사
sqlSession.insert("salesMng.insertProposalPartFromMBom", partParam);
} else {
// 수동 작성 -> SALES_REQUEST_PART에서 복사
sqlSession.insert("salesMng.insertProposalPartFromManual", partParam);
}
}
// 5. 원본 데이터의 PROPOSAL_DATE 업데이트
Map updateParam = new HashMap();
updateParam.put("PART_OBJIDS", sourceObjids);
// M-BOM 기반 여부에 따라 업데이트 쿼리 분기
String updateQuery = mbomHeaderObjid.isEmpty()
? "salesMng.updateProposalDateForManualParts" // 수동 작성
: "salesMng.updateProposalDateForMBomParts"; // M-BOM 기반
sqlSession.update(updateQuery, updateParam);
sqlSession.commit();
resultMap.put("resultFlag", "S");
resultMap.put("message", "품의서가 생성되었습니다. (품의서 번호: " + proposalNo + ")");
resultMap.put("PROPOSAL_OBJID", proposalMaster.get("OBJID"));
resultMap.put("PROPOSAL_NO", proposalNo);
} 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;
}
}