auto commit

This commit is contained in:
leeheejin
2025-11-17 17:29:04 +09:00
parent c1921855e6
commit fd4283f3cf
21 changed files with 2987 additions and 99 deletions

View File

@@ -2530,18 +2530,18 @@ SELECT option_objid::VARCHAR AS CODE
AND PRODUCT_MGMT_OBJID::VARCHAR = #{codeId}::VARCHAR
</select>
<select id="getRevNoselect" parameterType="map" resultType="map">
SELECT
T.OBJID::varchar AS CODE
,T.REV AS NAME
,T1.PRODUCT_CODE AS CODE_CD
,'' AS STATUS
,T.spec_name AS ID
,'' AS EXT_VAL
FROM PART_BOM_REPORT T,PRODUCT_MGMT T1
WHERE T.PRODUCT_MGMT_OBJID = T1.OBJID
AND T1.OBJID = #{code}::numeric
ORDER BY T.REV
<select id="getRevNoselect" parameterType="map" resultType="map">
SELECT
T.OBJID::varchar AS CODE
,COALESCE(T.REVISION, T.REV, '1.0') AS NAME
,T1.PRODUCT_CODE AS CODE_CD
,'' AS STATUS
,T.spec_name AS ID
,'' AS EXT_VAL
FROM PART_BOM_REPORT T,PRODUCT_MGMT T1
WHERE T.PRODUCT_MGMT_OBJID = T1.OBJID
AND T1.OBJID = #{code}::numeric
ORDER BY COALESCE(T.REVISION, T.REV, '1.0')
</select>

View File

@@ -2870,8 +2870,9 @@
ORDER BY SUBSTRING(PROJECT_NO,POSITION('-' IN PROJECT_NO)+1) DESC
</select>
<!-- M-BOM 관리 목록 조회 -->
<!-- M-BOM 관리 목록 조회 - 품목별로 나눠서 보이기 (판매관리와 동일) -->
<select id="mBomMgmtGridList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
/* productionplanning.mBomMgmtGridList - 품목별로 나눠서 조회 (CONTRACT_ITEM 기준) */
SELECT
PM.OBJID,
PM.CONTRACT_OBJID,
@@ -2906,54 +2907,40 @@
ELSE ''
END
) AS PAID_TYPE_NAME,
COALESCE(PM.PART_NO, '') AS PART_NO,
COALESCE(PM.PART_NAME, '') AS PART_NAME,
-- S/N: CONTRACT_ITEM_SERIAL에서 조회
-- 품목 정보: CONTRACT_ITEM에서 가져오기 (품목별로 펼쳐서 보이기)
COALESCE(CI.PART_NO, PM.PART_NO, '') AS PART_NO,
COALESCE(CI.PART_NAME, PM.PART_NAME, '') AS PART_NAME,
CI.PART_OBJID,
-- S/N: 해당 품목의 시리얼 번호만 표시
(SELECT
CASE
WHEN COUNT(*) = 0 THEN ''
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
END
FROM CONTRACT_ITEM CI
LEFT JOIN CONTRACT_ITEM_SERIAL CIS ON CI.OBJID = CIS.ITEM_OBJID AND UPPER(CIS.STATUS) = 'ACTIVE'
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
AND CI.PART_OBJID = PM.PART_OBJID
AND CI.STATUS = 'ACTIVE'
FROM CONTRACT_ITEM_SERIAL CIS
WHERE CIS.ITEM_OBJID = CI.OBJID
AND UPPER(CIS.STATUS) = 'ACTIVE'
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
COALESCE(PM.QUANTITY::numeric, 0) AS QUANTITY,
-- 요청납기: CONTRACT_ITEM 우선, 없으면 PROJECT_MGMT, 없으면 CONTRACT_MGMT
COALESCE(
(SELECT CI.DUE_DATE
FROM CONTRACT_ITEM CI
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
AND CI.PART_OBJID = PM.PART_OBJID
AND CI.STATUS = 'ACTIVE' LIMIT 1),
PM.DUE_DATE,
CM.req_del_date
) AS REQ_DEL_DATE,
-- 수량: CONTRACT_ITEM의 수량
COALESCE(CI.ORDER_QUANTITY::numeric, PM.QUANTITY::numeric, 0) AS QUANTITY,
-- 요청납기: CONTRACT_ITEM 우선
COALESCE(CI.DUE_DATE, PM.DUE_DATE, CM.req_del_date) AS REQ_DEL_DATE,
-- 고객요청사항: CONTRACT_ITEM에서 가져옴
COALESCE(
(SELECT CI.CUSTOMER_REQUEST
FROM CONTRACT_ITEM CI
WHERE CI.CONTRACT_OBJID = PM.CONTRACT_OBJID
AND CI.PART_OBJID = PM.PART_OBJID
AND CI.STATUS = 'ACTIVE' LIMIT 1),
''
) AS CUSTOMER_REQUEST,
-- E-BOM 정보: PM.PART_OBJID가 E-BOM OBJID를 직접 가리킴
PM.PART_OBJID AS BOM_REPORT_OBJID,
COALESCE(CI.CUSTOMER_REQUEST, '') AS CUSTOMER_REQUEST,
-- E-BOM 정보: CI.PART_OBJID가 E-BOM OBJID를 가리킴
COALESCE(CI.PART_OBJID, PM.PART_OBJID) AS BOM_REPORT_OBJID,
COALESCE(
(SELECT PBR.STATUS
FROM PART_BOM_REPORT PBR
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
WHERE PBR.OBJID::VARCHAR = COALESCE(CI.PART_OBJID, PM.PART_OBJID)
LIMIT 1),
''
) AS EBOM_STATUS,
COALESCE(
(SELECT TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD')
FROM PART_BOM_REPORT PBR
WHERE PBR.OBJID::VARCHAR = PM.PART_OBJID
WHERE PBR.OBJID::VARCHAR = COALESCE(CI.PART_OBJID, PM.PART_OBJID)
LIMIT 1),
''
) AS EBOM_REGDATE,
@@ -2963,18 +2950,27 @@
FROM
PROJECT_MGMT PM
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
-- CONTRACT_ITEM과 LEFT JOIN하여 품목별로 펼쳐서 보이기
LEFT JOIN CONTRACT_ITEM CI ON CM.OBJID::VARCHAR = CI.CONTRACT_OBJID
AND CI.STATUS = 'ACTIVE'
WHERE 1=1
AND PM.PROJECT_NO IS NOT NULL
AND PM.PROJECT_NO != ''
<!-- 품번 검색 (대소문자 구분 없음) -->
<if test="search_part_no != null and search_part_no != ''">
AND UPPER(PM.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
AND (
UPPER(PM.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
OR UPPER(CI.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
)
</if>
<!-- 품명 검색 (대소문자 구분 없음) -->
<if test="search_part_name != null and search_part_name != ''">
AND UPPER(PM.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
AND (
UPPER(PM.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
OR UPPER(CI.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
)
</if>
ORDER BY PM.REGDATE DESC
ORDER BY PM.REGDATE DESC, CI.PART_NO
</select>
<!-- E-BOM을 PROJECT_MGMT에 할당 -->
@@ -3003,4 +2999,299 @@
LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER
WHERE T.OBJID::VARCHAR = #{objid}
</select>
<!-- M-BOM 상세 조회 (PROJECT_MGMT + CONTRACT_MGMT 정보) -->
<select id="getProjectMgmtDetail" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
PM.OBJID,
PM.CONTRACT_OBJID,
PM.PROJECT_NO,
PM.BOM_REPORT_OBJID,
PM.PART_NO,
PM.PART_NAME,
CM.CATEGORY_CD,
COALESCE(
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.CATEGORY_CD LIMIT 1),
''
) AS CATEGORY_NAME,
CM.PRODUCT,
COALESCE(
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PRODUCT LIMIT 1),
''
) AS PRODUCT_NAME,
CM.AREA_CD,
COALESCE(
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.AREA_CD LIMIT 1),
''
) AS AREA_NAME,
CM.CUSTOMER_OBJID,
COALESCE(
(SELECT SUPPLY_NAME FROM SUPPLY_MNG WHERE OBJID = CM.CUSTOMER_OBJID::NUMERIC LIMIT 1),
''
) AS CUSTOMER_NAME,
CM.PAID_TYPE,
CM.REQ_DEL_DATE,
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS RECEIPT_DATE,
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS MBOM_REGDATE
FROM
PROJECT_MGMT PM
INNER JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
WHERE
PM.OBJID::VARCHAR = #{objId}
LIMIT 1
</select>
<!-- E-BOM 목록 조회 -->
<select id="getEbomList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
T.OBJID,
T.PRODUCT_CD,
COALESCE(
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = T.PRODUCT_CD LIMIT 1),
''
) AS PRODUCT_NAME,
T.PART_NO,
T.PART_NAME,
T.STATUS,
T.REVISION,
TO_CHAR(T.REGDATE, 'YYYY-MM-DD') AS REG_DATE,
UI.USER_NAME AS WRITER_NAME,
UI.DEPT_NAME,
COALESCE(PM.MATERIAL, '') AS MATERIAL,
COALESCE(PM.MAKER, '') AS SUPPLIER
FROM
PART_BOM_REPORT T
LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER
LEFT JOIN PART_MNG PM ON PM.PART_NO = T.PART_NO AND PM.STATUS = 'release'
WHERE 1=1
<!-- 품번, 품명이 비어있지 않은 것만 조회 -->
AND T.PART_NO IS NOT NULL
AND TRIM(T.PART_NO) != ''
AND T.PART_NAME IS NOT NULL
AND TRIM(T.PART_NAME) != ''
<!-- 품번 검색 -->
<if test="search_part_no != null and search_part_no != ''">
AND UPPER(T.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
</if>
<!-- 품명 검색 -->
<if test="search_part_name != null and search_part_name != ''">
AND UPPER(T.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
</if>
<!-- 재료 검색 -->
<if test="search_material != null and search_material != ''">
AND UPPER(PM.MATERIAL) LIKE '%' || UPPER(#{search_material}) || '%'
</if>
<!-- 공급업체 검색 -->
<if test="search_supplier != null and search_supplier != ''">
AND UPPER(PM.MAKER) LIKE '%' || UPPER(#{search_supplier}) || '%'
</if>
ORDER BY T.REGDATE DESC
LIMIT 100
</select>
<!-- M-BOM 목록 조회 -->
<select id="getMbomList" resultType="map" parameterType="map">
SELECT
MH.OBJID,
MH.MBOM_NO,
MH.PART_NO,
MH.PART_NAME,
MH.SAVE_DATE,
MH.CREATE_USER,
MH.CREATE_DATE,
MH.UPDATE_USER,
MH.UPDATE_DATE
FROM
MBOM_HEADER MH
WHERE 1=1
<!-- 품번 검색 -->
<if test="partNo != null and partNo != ''">
AND UPPER(MH.PART_NO) LIKE '%' || UPPER(#{partNo}) || '%'
</if>
<!-- 품명 검색 -->
<if test="partName != null and partName != ''">
AND UPPER(MH.PART_NAME) LIKE '%' || UPPER(#{partName}) || '%'
</if>
<!-- M-BOM 품번 검색 -->
<if test="mbomPartNo != null and mbomPartNo != ''">
AND UPPER(MH.MBOM_NO) LIKE '%' || UPPER(#{mbomPartNo}) || '%'
</if>
<!-- 저장일 검색 -->
<if test="saveDate != null and saveDate != ''">
AND DATE(MH.SAVE_DATE) = #{saveDate}
</if>
ORDER BY MH.SAVE_DATE DESC
LIMIT 100
</select>
<!-- M-BOM 상세 데이터 조회 (프로젝트별) -->
<select id="getMbomDetailByProject" resultType="map" parameterType="map">
SELECT
MD.OBJID,
MD.MBOM_HEADER_OBJID,
MD.PART_NO,
MD.PART_NAME,
MD.QTY,
MD.QTY_TEMP,
MD.ITEM_QTY,
MD.LEVEL,
MD.REVISION,
MD.SPEC,
MD.PRODUCT_NAME,
MD.STATUS_NAME,
MD.LEVEL_1,
MD.LEVEL_2,
MD.LEVEL_3,
MD.LEVEL_4,
MD.LEVEL_5,
MD.LEVEL_6,
MD.LEVEL_7,
MD.LEVEL_8,
MD.LEVEL_9,
MD.LEVEL_10
FROM
MBOM_DETAIL MD
INNER JOIN
MBOM_HEADER MH ON MD.MBOM_HEADER_OBJID = MH.OBJID
WHERE
MH.PROJECT_MGMT_OBJID = #{objId}
ORDER BY
MD.LEVEL, MD.PART_NO
</select>
<!-- M-BOM 헤더 저장 -->
<insert id="insertMbomHeader" parameterType="map">
<selectKey keyProperty="OBJID" resultType="string" order="BEFORE">
SELECT REPLACE(UUID(),'-','') FROM DUAL
</selectKey>
INSERT INTO MBOM_HEADER (
OBJID,
MBOM_NO,
PART_NO,
PART_NAME,
SAVE_DATE,
CREATE_USER,
CREATE_DATE,
UPDATE_USER,
UPDATE_DATE
) VALUES (
#{OBJID},
CONCAT('MBOM-', DATE_FORMAT(NOW(), '%Y%m%d%H%i%s')),
#{PART_NO},
#{PART_NAME},
NOW(),
#{CREATE_USER},
NOW(),
#{UPDATE_USER},
NOW()
)
</insert>
<!-- M-BOM 상세 저장 -->
<insert id="insertMbomDetail" parameterType="map">
<selectKey keyProperty="OBJID" resultType="string" order="BEFORE">
SELECT REPLACE(UUID(),'-','') FROM DUAL
</selectKey>
INSERT INTO MBOM_DETAIL (
OBJID,
MBOM_HEADER_OBJID,
PART_NO,
PART_NAME,
QTY,
LEVEL,
REVISION,
SPEC,
PRODUCT_NAME,
STATUS_NAME,
CREATE_USER,
CREATE_DATE,
UPDATE_USER,
UPDATE_DATE
) VALUES (
#{OBJID},
#{MBOM_HEADER_OBJID},
#{PART_NO},
#{PART_NAME},
#{QTY},
#{LEVEL},
#{REVISION},
#{SPEC},
#{PRODUCT_NAME},
#{STATUS_NAME},
#{CREATE_USER},
NOW(),
#{UPDATE_USER},
NOW()
)
</insert>
<!-- E-BOM을 M-BOM으로 복사 -->
<insert id="saveMbomFromEbom" parameterType="map">
/* productionplanning.saveMbomFromEbom - E-BOM을 M-BOM으로 복사 */
/* 1. 새로운 PART_BOM_REPORT 생성 (M-BOM용) */
WITH new_bom_report AS (
INSERT INTO PART_BOM_REPORT (
OBJID,
PART_NO,
PART_NAME,
WRITER,
REGDATE,
STATUS
) VALUES (
(SELECT (FLOOR(RANDOM() * 1000000000) + 1000000000)::INTEGER),
#{PART_NO},
#{PART_NAME},
#{USER_ID},
NOW(),
'Y'
)
RETURNING OBJID
),
/* 2. E-BOM의 BOM_PART_QTY 데이터를 새 M-BOM으로 복사 */
copied_bom_data AS (
INSERT INTO BOM_PART_QTY (
BOM_REPORT_OBJID,
OBJID,
PARENT_OBJID,
CHILD_OBJID,
PARENT_PART_NO,
PART_NO,
QTY,
ITEM_QTY,
QTY_TEMP,
REGDATE,
WRITER,
SEQ,
STATUS,
LAST_PART_OBJID
)
SELECT
(SELECT OBJID FROM new_bom_report),
(SELECT (FLOOR(RANDOM() * 1000000000) + 1000000000 + ROW_NUMBER() OVER (ORDER BY OBJID))::INTEGER),
PARENT_OBJID,
CHILD_OBJID,
PARENT_PART_NO,
PART_NO,
QTY,
ITEM_QTY,
QTY_TEMP,
NOW(),
#{USER_ID},
ROW_NUMBER() OVER (ORDER BY OBJID),
'adding',
LAST_PART_OBJID
FROM BOM_PART_QTY
WHERE BOM_REPORT_OBJID::VARCHAR = #{SOURCE_BOM_OBJID}
AND COALESCE(STATUS, '') NOT IN ('deleting', 'deleted')
RETURNING 1
)
/* 3. PROJECT_MGMT 업데이트 */
UPDATE PROJECT_MGMT
SET
BOM_REPORT_OBJID = (SELECT OBJID FROM new_bom_report),
MBOM_STATUS = 'Y',
PART_NO = #{PART_NO},
PART_NAME = #{PART_NAME}
WHERE OBJID::VARCHAR = #{PROJECT_OBJID}
</insert>
</mapper>

View File

@@ -134,6 +134,14 @@ var selectedBomType = null; // 'EBOM' 또는 'MBOM'
var bomGridData = []; // BOM 그리드 데이터
$(function(){
// 페이지 로드 시 프로젝트 정보가 있으면 품번/품명 자동 입력
<c:if test="${not empty projectInfo}">
console.log("projectInfo가 있습니다. 품번/품명 설정 중...");
$("#COPY_PART_NO").val("${projectInfo.PART_NO}");
$("#COPY_PART_NAME").val("${projectInfo.PART_NAME}");
// E-BOM 품번은 사용자가 직접 입력하도록 비워둠 (M-BOM 품번과 다를 수 있음)
</c:if>
// 담기 버튼 - 선택한 BOM을 복사 대상으로 설정
$("#btnAddItem").click(function(){
var partNo = $("#COPY_PART_NO").val().trim();
@@ -207,6 +215,8 @@ $(function(){
// BOM 미리보기 로드
function fn_loadBomPreview(partNo, bomType) {
console.log("fn_loadBomPreview 호출:", partNo, bomType);
// 먼저 품번으로 BOM OBJID 조회
$.ajax({
url: "/partMng/getBomObjIdByPartNo.do",
@@ -214,6 +224,8 @@ function fn_loadBomPreview(partNo, bomType) {
data: { partNo: partNo },
dataType: "json",
success: function(response) {
console.log("getBomObjIdByPartNo 응답:", response);
if(response && response.OBJID) {
$("#bomPartName").text(partNo);
// 전역 변수에 저장
@@ -222,10 +234,12 @@ function fn_loadBomPreview(partNo, bomType) {
// BOM 트리 데이터 로드
fn_loadBomTree(response.OBJID);
} else {
console.error("BOM OBJID를 찾을 수 없음:", response);
Swal.fire('해당 품번의 BOM을 찾을 수 없습니다.');
}
},
error: function() {
error: function(xhr, status, error) {
console.error("getBomObjIdByPartNo 에러:", xhr, status, error);
Swal.fire('BOM 조회 중 오류가 발생했습니다.');
}
});
@@ -233,6 +247,8 @@ function fn_loadBomPreview(partNo, bomType) {
// BOM 트리 데이터 로드
function fn_loadBomTree(bomObjId) {
console.log("fn_loadBomTree 호출:", bomObjId);
$.ajax({
url: "/partMng/getStructureTreeJson.do",
type: "POST",
@@ -243,6 +259,8 @@ function fn_loadBomTree(bomObjId) {
},
dataType: "json",
success: function(response) {
console.log("getStructureTreeJson 응답:", response);
if(response && response.length > 0) {
var maxLevel = response[0].MAX_LEVEL || 3;
@@ -260,11 +278,13 @@ function fn_loadBomTree(bomObjId) {
fn_initGrid(processedData, maxLevel);
} else {
console.warn("BOM 트리 데이터가 비어있음");
bomGridData = [];
fn_initGrid([], 3);
}
},
error: function() {
error: function(xhr, status, error) {
console.error("getStructureTreeJson 에러:", xhr, status, error);
Swal.fire('BOM 트리 조회 중 오류가 발생했습니다.');
}
});
@@ -506,6 +526,10 @@ function fn_saveBomCopy() {
text: 'M-BOM이 성공적으로 생성되었습니다.',
icon: 'success'
}).then(() => {
// 부모 창(M-BOM 관리) 새로고침하여 아이콘 업데이트
if(window.opener && !window.opener.closed) {
window.opener.location.reload();
}
window.close();
});
} else {
@@ -607,11 +631,6 @@ function fn_excel() {
<div id="structureGrid"></div>
</div>
<!-- 하단: 저장/닫기 버튼 -->
<div class="bottom-button-section">
<input type="button" value="저장" class="plm_btns" id="btnSaveItem" style="margin-right: 5px;">
<input type="button" value="닫기" class="plm_btns" id="btnClose" onclick="fn_close();">
</div>
</form>
</body>
</html>

View File

@@ -422,16 +422,14 @@ function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPa
}
</script>
</head>
<body class="backcolor" style="border:border:1px solid #ccc;">
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
<div id="structurePopupBtnW" style="padding-top:20px;">
<div id="structurePopupBtnW">
<input type="button" value="변경" class="plm_btns" id="moveChange">
<br>
<input type="button" value="<<" class="plm_btns" id="moveLeft" style="margin-left:0px;">
<br>
<input type="button" value=">>" class="plm_btns" id="moveRight" style="margin-left:0px;">
<input type="button" value="<<" class="plm_btns" id="moveLeft">
<input type="button" value=">>" class="plm_btns" id="moveRight">
</div>
</form>
</body>

View File

@@ -0,0 +1,439 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<script>
// confirm/alert 헬퍼 함수
function showConfirm(options) {
if(typeof options === 'string') {
alert(options);
return Promise.resolve({isConfirmed: true});
}
var message = '';
if(options.title) message += options.title + '\n\n';
if(options.html) {
// HTML 태그 제거
message += options.html.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '');
} else if(options.text) {
message += options.text;
}
if(options.showCancelButton !== false && (options.icon === 'warning' || options.icon === 'question')) {
var result = confirm(message);
return Promise.resolve({isConfirmed: result});
} else {
alert(message);
return Promise.resolve({isConfirmed: true});
}
}
$(function(){
$('.select2').select2();
//Part 연결
$("#moveLeft").click(function(){
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
var rightFrame = parent.frames['rightFrame'];
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
if(rightSelectedRows.length === 0) {
showConfirm("선택된 파트가 없습니다.");
return false;
}
// 왼쪽 프레임에서 선택된 행 데이터 가져오기
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
var leftPartChildObjId = null;
var leftPartNo = null;
var leftPartNoQty = null;
var leftParentObjId = null;
var leftPartLastObjId = null;
var leftQtyParObjId = null;
var leftParentParts = "";
if(leftPartNoObj.length > 0) {
leftPartChildObjId = leftPartNoObj.val();
leftPartNo = leftPartNoObj.attr("data-PART_NO");
leftPartNoQty = leftPartNoObj.attr("data-PART_NO_QTY");
leftParentObjId = leftPartNoObj.attr("data-OBJID");
leftPartLastObjId = leftPartNoObj.attr("data-LAST_PART_OBJID");
leftQtyParObjId = leftPartNoObj.attr("data-PART_OBJID");
leftParentParts = leftPartNoObj.attr("data-PARENT_PARTS") || "";
}
// 같은 Part를 연결한건지 체크
var isSamePart = false;
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
var rightPartNo = rowData.PART_NO;
if(rightPartNo == leftPartNo){
showConfirm({
title: '연결 불가',
html: '오류 Part No : <strong>['+rightPartNo+']</strong><br>같은 Part No끼리 연결할 수 없습니다.',
icon: 'error'
});
isSamePart = true;
break;
}
}
if(isSamePart) return false;
// 연결하려는 part가 상위에 있는 part인지 확인
var deniedPartArr = [];
if(fnc_checkNull(leftParentParts).indexOf(",") > 0){
deniedPartArr = leftParentParts.split(",");
}
var isDeniedPart = false;
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
var rightPartNo = rowData.PART_NO;
var rightPartType = rowData.PART_TYPE;
if("unique" == rightPartType){
for(var j = 0 ; j < deniedPartArr.length ; j++){
if(rightPartNo == deniedPartArr[j]){
showConfirm({
title: '연결 불가',
html: '오류 Part No : <strong>['+rightPartNo+']</strong><br>이미 상위에 등록된 Part No 입니다.',
icon: 'error'
});
isDeniedPart = true;
break;
}
}
if(isDeniedPart) break;
}
}
if(isDeniedPart) return;
// 선택된 파트의 OBJID 배열 생성
var rightCheckedArr = [];
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
rightCheckedArr.push(rowData.OBJID);
}
if(fnc_checkNull(leftPartNo) == ""){
var flag = fn_checkSameTopPartNo(rightCheckedArr);
if(flag == "true"){
showConfirm({
title: '중복 등록 불가',
text: '1레벨에 같은 Part No가 중복 등록될 수 없습니다.',
icon: 'error'
});
return;
}
}
// Part 연결 확인
showConfirm({
title: 'Part 연결',
text: '선택한 Part를 연결하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '연결',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_relatePartInfo(leftPartChildObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
});
});
//end of Part 연결
//연결된 part 삭제
$("#moveRight").click(function(){
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
var leftPartChildObjId = leftPartNoObj.val();
var leftPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO");
var leftParentPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_PART_NO");
var leftParentObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_OBJID");
var leftPartLastObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-LAST_PART_OBJID");
fn_deletePartRelateInfo(leftPartNoObj.val(), leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId);
});
//end of 연결된 part 삭제
//연결된 part 변경
$("#moveChange").click(function(){
var leftPartNoList = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
if(leftPartNoList.length === 0){
showConfirm("선택된 파트가 없습니다.");
return false;
}
if(leftPartNoList.length > 1){
showConfirm("한번에 1개의 파트만 변경가능합니다.");
return false;
}
var leftPartNo = leftPartNoList.attr("data-PART_NO");
var leftPartObjid = leftPartNoList.attr("data-BOM_LAST_PART_OBJID");
var leftParentPartObjid = leftPartNoList.attr("data-PARENT_PART_NO");
var leftPartChildObjId = leftPartNoList.val();
var leftPartBomQtyObjId = leftPartNoList.attr("data-OBJID");
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
var rightFrame = parent.frames['rightFrame'];
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
if(rightSelectedRows.length === 0){
showConfirm("선택된 파트가 없습니다.");
return false;
}
if(rightSelectedRows.length > 1){
showConfirm("한번에 1개의 파트만 변경가능합니다.");
return false;
}
var rightRowData = rightSelectedRows[0].getData();
var rightPartNo = rightRowData.PART_NO;
var rightPartRev = rightRowData.REVISION || "";
var rightObjId = rightRowData.OBJID;
// Part 변경 확인
showConfirm({
title: 'Part 변경',
html: '선택한 Part를 변경하시겠습니까?<br><br>' +
'<strong>기존:</strong> ' + leftPartNo + '<br>' +
'<strong>변경:</strong> ' + rightPartNo,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '변경',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_changeRelatePartInfo(leftPartBomQtyObjId, rightObjId, leftPartChildObjId, leftParentPartObjid, leftPartChildObjId, leftPartObjid, rightPartNo, rightPartRev);
}
});
});
});
//1레벨에 같은 Part No가 등록되어있는지 확인.
function fn_checkSameTopPartNo(rightCheckedArr){
var result = false;
$.ajax({
url: "/productionplanning/checkSameTopPartNo.do",
method: 'post',
traditional: true, // 배열 파라미터 처리를 위한 옵션
data: {"OBJID":$("#objId").val(), "rightCheckedArr[]":rightCheckedArr}, // [] 추가
dataType: 'json',
async:false,
success: function(data) {
result = data.result;
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
return result;
}
//end of 1레벨에 같은 Part No가 등록되어있는지 확인.
//구조 연결 해제
function fn_deletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId){
if(leftObjId == null){
showConfirm("연결 해제할 Part를 선택해 주시기 바랍니다.");
return;
}
showConfirm({
title: 'Part 연결 해제',
text: '선택한 Part의 연결을 해제하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '연결 해제',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (!result.isConfirmed) return;
fn_executeDeletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId);
});
}
// 실제 Part 연결 해제 실행 함수
function fn_executeDeletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId){
$.ajax({
url: "/productionplanning/deleteStatusPartRelateInfo.do",
method: 'post',
data: {"OBJID":$("#objId").val(), "leftObjId":leftObjId, "partObjId":leftPartLastObjId, "BOM_REPORT_OBJID":$("#objId").val()
, "leftPartChildObjId":leftPartChildObjId, "leftParentPartNo":leftParentPartNo, "leftParentObjId":leftParentObjId},
dataType: 'json',
success: function(data) {
if(data.result){
$(parent.frames['leftFrame'].document.location.reload());
//$(parent.frames['rightFrame'].fn_searchPart());
// 부모 창(M-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
//end of 구조 연결 해제
//구조 연결
function fn_relatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId){
if(typeof rightCheckedArr != "undefined" && rightCheckedArr.length == 0){
showConfirm("선택된 Part가 없습니다.");
return;
}
if(leftObjId == null){
showConfirm({
title: '1레벨 등록 확인',
html: '좌측에 선택된 Part정보가 없습니다.<br>이대로 연결하면 1레벨로 등록됩니다.<br><br>진행하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '진행',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
});
return;
}
fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
// 실제 Part 연결 실행 함수
function fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId){
$.ajax({
url: "/productionplanning/relatePartInfo.do",
method: 'post',
traditional: true, // 배열 파라미터 처리를 위한 옵션
data: {
"leftObjId": leftObjId,
"leftPartNoQty": leftPartNoQty,
"OBJID": $("#objId").val(),
"rightCheckedArr[]": rightCheckedArr, // Spring의 @RequestParam(value = "rightCheckedArr[]") 형식
"partObjId": leftPartLastObjId,
"BOM_REPORT_OBJID": $("#objId").val(),
"leftPartChildObjId": leftPartChildObjId,
"leftQtyParObjId": leftQtyParObjId
},
dataType: 'json',
async:false,
success: function(data) {
if(data.result){
// 왼쪽 프레임 새로고침
$(parent.frames['leftFrame'].document.location.reload());
// 오른쪽 프레임 선택 해제 (Tabulator)
var rightFrame = parent.frames['rightFrame'];
if(rightFrame.clearSelection) {
rightFrame.clearSelection();
}
// 부모 창(M-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
//end of 구조 연결
//구조 연결 변경
function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPartChildObjId,leftPartObjid,rightPartNo,rightPartRev){
$.ajax({
url: "/productionplanning/changeRelatePartInfo.do",
method: 'post',
data: {"rightObjId":rightObjId, "OBJID":objId,"BOM_REPORT_OBJID":$("#objId").val(),
"leftObjId":leftObjId,"leftPartNoQty":leftPartNoQty,"leftPartChildObjId":leftPartChildObjId,
"leftPartObjid":leftPartObjid, "rightPartNo":rightPartNo, "rightPartRev":rightPartRev},
dataType: 'json',
async:false,
success: function(data) {
if(data.result){
// 왼쪽 프레임 새로고침
$(parent.frames['leftFrame'].document.location.reload());
// 오른쪽 프레임 검색 다시 수행
var rightFrame = parent.frames['rightFrame'];
if(rightFrame.fn_searchPart) {
rightFrame.fn_searchPart();
}
// 오른쪽 프레임 선택 해제 (Tabulator)
if(rightFrame.clearSelection) {
rightFrame.clearSelection();
}
// 부모 창(M-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
</script>
</head>
<body class="backcolor" style="border:border:1px solid #ccc;">
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
<div id="structurePopupBtnW" style="padding-top:20px;">
<input type="button" value="변경" class="plm_btns" id="moveChange">
<br>
<input type="button" value="<<" class="plm_btns" id="moveLeft" style="margin-left:0px;">
<br>
<input type="button" value=">>" class="plm_btns" id="moveRight" style="margin-left:0px;">
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,584 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
margin: 0;
padding: 0;
overflow: hidden;
height: auto;
}
#plmSearchZon td.label,
#plmSearchZon td:first-child {
text-align: right;
padding-right: 5px;
}
</style>
<script>
// confirm/alert 헬퍼 함수
function showConfirm(options) {
if(typeof options === 'string') {
alert(options);
return Promise.resolve({isConfirmed: true});
}
var message = '';
if(options.title) message += options.title + '\n\n';
if(options.html) {
// HTML 태그 제거
message += options.html.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '');
} else if(options.text) {
message += options.text;
}
if(options.showCancelButton !== false && (options.icon === 'warning' || options.icon === 'question')) {
var result = confirm(message);
return Promise.resolve({isConfirmed: result});
} else {
alert(message);
return Promise.resolve({isConfirmed: true});
}
}
$(function(){
$('.select2').select2();
//Part 연결
$("#moveLeft").click(function(){
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
var rightFrame = parent.frames['rightFrame'];
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
if(rightSelectedRows.length === 0) {
showConfirm("선택된 파트가 없습니다.");
return false;
}
// 왼쪽 프레임에서 선택된 행 데이터 가져오기
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
var leftPartChildObjId = null;
var leftPartNo = null;
var leftPartNoQty = null;
var leftParentObjId = null;
var leftPartLastObjId = null;
var leftQtyParObjId = null;
var leftParentParts = "";
if(leftPartNoObj.length > 0) {
leftPartChildObjId = leftPartNoObj.val();
leftPartNo = leftPartNoObj.attr("data-PART_NO");
leftPartNoQty = leftPartNoObj.attr("data-PART_NO_QTY");
leftParentObjId = leftPartNoObj.attr("data-OBJID");
leftPartLastObjId = leftPartNoObj.attr("data-LAST_PART_OBJID");
leftQtyParObjId = leftPartNoObj.attr("data-PART_OBJID");
leftParentParts = leftPartNoObj.attr("data-PARENT_PARTS") || "";
}
// 같은 Part를 연결한건지 체크
var isSamePart = false;
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
var rightPartNo = rowData.PART_NO;
if(rightPartNo == leftPartNo){
showConfirm({
title: '연결 불가',
html: '오류 Part No : <strong>['+rightPartNo+']</strong><br>같은 Part No끼리 연결할 수 없습니다.',
icon: 'error'
});
isSamePart = true;
break;
}
}
if(isSamePart) return false;
// 연결하려는 part가 상위에 있는 part인지 확인
var deniedPartArr = [];
if(fnc_checkNull(leftParentParts).indexOf(",") > 0){
deniedPartArr = leftParentParts.split(",");
}
var isDeniedPart = false;
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
var rightPartNo = rowData.PART_NO;
var rightPartType = rowData.PART_TYPE;
if("unique" == rightPartType){
for(var j = 0 ; j < deniedPartArr.length ; j++){
if(rightPartNo == deniedPartArr[j]){
showConfirm({
title: '연결 불가',
html: '오류 Part No : <strong>['+rightPartNo+']</strong><br>이미 상위에 등록된 Part No 입니다.',
icon: 'error'
});
isDeniedPart = true;
break;
}
}
if(isDeniedPart) break;
}
}
if(isDeniedPart) return;
// 선택된 파트의 OBJID 배열 생성
var rightCheckedArr = [];
for(var i = 0; i < rightSelectedRows.length; i++){
var rowData = rightSelectedRows[i].getData();
rightCheckedArr.push(rowData.OBJID);
}
if(fnc_checkNull(leftPartNo) == ""){
var flag = fn_checkSameTopPartNo(rightCheckedArr);
if(flag == "true"){
showConfirm({
title: '중복 등록 불가',
text: '1레벨에 같은 Part No가 중복 등록될 수 없습니다.',
icon: 'error'
});
return;
}
}
// Part 연결 확인
showConfirm({
title: 'Part 연결',
text: '선택한 Part를 연결하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '연결',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_relatePartInfo(leftPartChildObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
});
});
//end of Part 연결
//연결된 part 삭제
$("#moveRight").click(function(){
var leftPartNoObj = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
var leftPartChildObjId = leftPartNoObj.val();
var leftPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PART_NO");
var leftParentPartNo = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_PART_NO");
var leftParentObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-PARENT_OBJID");
var leftPartLastObjId = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document).attr("data-LAST_PART_OBJID");
fn_deletePartRelateInfo(leftPartNoObj.val(), leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId);
});
//end of 연결된 part 삭제
//연결된 part 변경
$("#moveChange").click(function(){
var leftPartNoList = $("input[name=checkedPartNo]:checked", parent.frames['leftFrame'].document);
if(leftPartNoList.length === 0){
showConfirm("선택된 파트가 없습니다.");
return false;
}
if(leftPartNoList.length > 1){
showConfirm("한번에 1개의 파트만 변경가능합니다.");
return false;
}
var leftPartNo = leftPartNoList.attr("data-PART_NO");
var leftPartObjid = leftPartNoList.attr("data-BOM_LAST_PART_OBJID");
var leftParentPartObjid = leftPartNoList.attr("data-PARENT_PART_NO");
var leftPartChildObjId = leftPartNoList.val();
var leftPartBomQtyObjId = leftPartNoList.attr("data-OBJID");
// Tabulator에서 선택된 오른쪽 행 데이터 가져오기
var rightFrame = parent.frames['rightFrame'];
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
if(rightSelectedRows.length === 0){
showConfirm("선택된 파트가 없습니다.");
return false;
}
if(rightSelectedRows.length > 1){
showConfirm("한번에 1개의 파트만 변경가능합니다.");
return false;
}
var rightRowData = rightSelectedRows[0].getData();
var rightPartNo = rightRowData.PART_NO;
var rightPartRev = rightRowData.REVISION || "";
var rightObjId = rightRowData.OBJID;
// Part 변경 확인
showConfirm({
title: 'Part 변경',
html: '선택한 Part를 변경하시겠습니까?<br><br>' +
'<strong>기존:</strong> ' + leftPartNo + '<br>' +
'<strong>변경:</strong> ' + rightPartNo,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '변경',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_changeRelatePartInfo(leftPartBomQtyObjId, rightObjId, leftPartChildObjId, leftParentPartObjid, leftPartChildObjId, leftPartObjid, rightPartNo, rightPartRev);
}
});
});
// 조회 버튼 클릭
$("#btnSearch").click(function(){
fn_searchMbom();
});
// 저장 버튼 클릭
$("#btnSave").click(function(){
fn_saveMbom();
});
// BOM 복사 버튼 클릭
$("#btnBomCopy").click(function(){
fn_openBomCopyPopup();
});
});
//1레벨에 같은 Part No가 등록되어있는지 확인.
function fn_checkSameTopPartNo(rightCheckedArr){
var result = false;
$.ajax({
url: "/productionplanning/checkSameTopPartNo.do",
method: 'post',
traditional: true, // 배열 파라미터 처리를 위한 옵션
data: {"OBJID":$("#objId").val(), "rightCheckedArr[]":rightCheckedArr}, // [] 추가
dataType: 'json',
async:false,
success: function(data) {
result = data.result;
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
return result;
}
//end of 1레벨에 같은 Part No가 등록되어있는지 확인.
//구조 연결 해제
function fn_deletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId){
if(leftObjId == null){
showConfirm("연결 해제할 Part를 선택해 주시기 바랍니다.");
return;
}
showConfirm({
title: 'Part 연결 해제',
text: '선택한 Part의 연결을 해제하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '연결 해제',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (!result.isConfirmed) return;
fn_executeDeletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId);
});
}
// 실제 Part 연결 해제 실행 함수
function fn_executeDeletePartRelateInfo(leftObjId, leftPartLastObjId, leftParentPartNo, leftParentObjId, leftPartChildObjId){
$.ajax({
url: "/productionplanning/deleteStatusPartRelateInfo.do",
method: 'post',
data: {"OBJID":$("#objId").val(), "leftObjId":leftObjId, "partObjId":leftPartLastObjId, "BOM_REPORT_OBJID":$("#objId").val()
, "leftPartChildObjId":leftPartChildObjId, "leftParentPartNo":leftParentPartNo, "leftParentObjId":leftParentObjId},
dataType: 'json',
success: function(data) {
if(data.result){
$(parent.frames['leftFrame'].document.location.reload());
//$(parent.frames['rightFrame'].fn_searchPart());
// 부모 창(E-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
//end of 구조 연결 해제
//구조 연결
function fn_relatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId){
if(typeof rightCheckedArr != "undefined" && rightCheckedArr.length == 0){
showConfirm("선택된 Part가 없습니다.");
return;
}
if(leftObjId == null){
showConfirm({
title: '1레벨 등록 확인',
html: '좌측에 선택된 Part정보가 없습니다.<br>이대로 연결하면 1레벨로 등록됩니다.<br><br>진행하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '진행',
cancelButtonText: '취소',
reverseButtons: false
}).then(result => {
if (result.isConfirmed) {
fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
});
return;
}
fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId);
}
// 실제 Part 연결 실행 함수
function fn_executeRelatePartInfo(leftObjId, rightCheckedArr, leftPartNoQty, leftPartLastObjId, leftPartChildObjId, leftQtyParObjId){
$.ajax({
url: "/productionplanning/relatePartInfo.do",
method: 'post',
traditional: true, // 배열 파라미터 처리를 위한 옵션
data: {
"leftObjId": leftObjId,
"leftPartNoQty": leftPartNoQty,
"OBJID": $("#objId").val(),
"rightCheckedArr[]": rightCheckedArr, // Spring의 @RequestParam(value = "rightCheckedArr[]") 형식
"partObjId": leftPartLastObjId,
"BOM_REPORT_OBJID": $("#objId").val(),
"leftPartChildObjId": leftPartChildObjId,
"leftQtyParObjId": leftQtyParObjId
},
dataType: 'json',
async:false,
success: function(data) {
if(data.result){
// 왼쪽 프레임 새로고침
$(parent.frames['leftFrame'].document.location.reload());
// 오른쪽 프레임 선택 해제 (Tabulator)
var rightFrame = parent.frames['rightFrame'];
if(rightFrame.clearSelection) {
rightFrame.clearSelection();
}
// 부모 창(E-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
//end of 구조 연결
//구조 연결 변경
function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPartChildObjId,leftPartObjid,rightPartNo,rightPartRev){
$.ajax({
url: "/productionplanning/changeRelatePartInfo.do",
method: 'post',
data: {"rightObjId":rightObjId, "OBJID":objId,"BOM_REPORT_OBJID":$("#objId").val(),
"leftObjId":leftObjId,"leftPartNoQty":leftPartNoQty,"leftPartChildObjId":leftPartChildObjId,
"leftPartObjid":leftPartObjid, "rightPartNo":rightPartNo, "rightPartRev":rightPartRev},
dataType: 'json',
async:false,
success: function(data) {
if(data.result){
// 왼쪽 프레임 새로고침
$(parent.frames['leftFrame'].document.location.reload());
// 오른쪽 프레임 검색 다시 수행
var rightFrame = parent.frames['rightFrame'];
if(rightFrame.fn_searchPart) {
rightFrame.fn_searchPart();
}
// 오른쪽 프레임 선택 해제 (Tabulator)
if(rightFrame.clearSelection) {
rightFrame.clearSelection();
}
// 부모 창(E-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
}
}
, error: function(jqxhr, status, error){
/* alert(jqxhr.statusText + ", " + status + ", " + error);
alert(jqxhr.status);
alert(jqxhr.responseText); */
}
});
}
// M-BOM 조회
function fn_searchMbom() {
var partNo = $("#search_part_no").val().trim();
var partName = $("#search_part_name").val().trim();
var mbomPartNo = $("#search_mbom_part_no").val().trim();
var saveDate = $("#search_save_date").val().trim();
// 왼쪽 프레임의 조회 함수 호출
var leftFrame = parent.frames['leftFrame'];
if(leftFrame && leftFrame.fn_searchMbom) {
leftFrame.fn_searchMbom({
partNo: partNo,
partName: partName,
mbomPartNo: mbomPartNo,
saveDate: saveDate
});
}
}
// M-BOM 저장
function fn_saveMbom() {
// 왼쪽 프레임에서 M-BOM 트리 데이터 가져오기
var leftFrame = parent.frames['leftFrame'];
if(!leftFrame) {
alert("M-BOM 데이터를 가져올 수 없습니다.");
return;
}
// 트리 데이터 수집 (leftFrame에서 구현 필요)
var mbomData = leftFrame.getMbomTreeData ? leftFrame.getMbomTreeData() : null;
if(!mbomData || mbomData.length === 0) {
alert("저장할 M-BOM 데이터가 없습니다.");
return;
}
// 저장 확인
if(!confirm("M-BOM을 저장하시겠습니까?")) {
return;
}
// 저장 API 호출
$.ajax({
url: "/productionplanning/saveMbom.do",
method: 'post',
data: JSON.stringify({
mbomData: mbomData,
partNo: $("#search_part_no").val().trim(),
partName: $("#search_part_name").val().trim()
}),
contentType: 'application/json',
dataType: 'json',
success: function(data) {
if(data && data.result === "success") {
alert("M-BOM이 저장되었습니다.");
// 조회 새로고침
fn_searchMbom();
// 부모 창 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
} else {
alert("M-BOM 저장에 실패했습니다.");
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 저장 오류:", error);
alert("M-BOM 저장 중 오류가 발생했습니다.");
}
});
}
// BOM 복사 팝업 열기
function fn_openBomCopyPopup() {
var objId = $("#objId").val();
if(!objId || objId === "-1") {
alert("프로젝트 정보를 찾을 수 없습니다.");
return;
}
// BOM 복사 팝업 열기 (저번에 만든 화면)
var url = "/partMng/structureBomCopyFormPopup.do?objId=" + objId;
var popupName = "bomCopyPopup";
var popupWidth = 1400;
var popupHeight = 800;
var left = (screen.width - popupWidth) / 2;
var top = (screen.height - popupHeight) / 2;
window.open(url, popupName, "width=" + popupWidth + ",height=" + popupHeight + ",left=" + left + ",top=" + top + ",scrollbars=yes,resizable=yes");
}
</script>
</head>
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
<div id="plmSearchZon">
<table>
<tr>
<td><label for="search_part_no">품번</label></td>
<td>
<input type="text" name="search_part_no" id="search_part_no" value="">
</td>
<td class="label"><label for="search_part_name">품명</label></td>
<td>
<input type="text" name="search_part_name" id="search_part_name" value="">
</td>
</tr>
<tr>
<td><label for="search_mbom_part_no">M-BOM 품번</label></td>
<td>
<input type="text" name="search_mbom_part_no" id="search_mbom_part_no" value="">
</td>
<td class="label"><label for="search_save_date">저장일</label></td>
<td>
<input type="date" name="search_save_date" id="search_save_date" value="">
</td>
<td>
<input type="button" value="조회" class="plm_btns" id="btnSearch">
<input type="button" value="저장" class="plm_btns" id="btnSave">
<input type="button" value="BOM 복사" class="plm_btns" id="btnBomCopy">
</td>
</tr>
</table>
</div>
</form>
</body>
</html>

View File

@@ -205,8 +205,20 @@ var columns = [
field: 'MBOM_STATUS',
formatter: fnc_subInfoValueFormatter,
cellClick: function(e, cell) {
var objid = fnc_checkNull(cell.getData().OBJID);
fn_openMBomPopup(objid);
var rowData = cell.getData();
var objid = fnc_checkNull(rowData.OBJID);
var mbomStatus = fnc_checkNull(rowData.MBOM_STATUS);
// 파란색(저장된 M-BOM)일 때만 팝업 열기
if(mbomStatus !== '' && mbomStatus !== '0') {
fn_openMBomFormPopup(objid);
} else {
Swal.fire({
title: '알림',
text: 'M-BOM이 생성되지 않았습니다.\nBOM 복사 버튼을 통해 먼저 M-BOM을 생성해주세요.',
icon: 'info'
});
}
}
},
@@ -271,12 +283,20 @@ function fn_openEBomSelectPopup(projectMgmtObjid, partNo, partName, bomReportObj
fn_centerPopup(popup_width, popup_height, url, 'ebomSelectPopup');
}
// M-BOM 팝업
// M-BOM 조회 팝업 (읽기 전용)
function fn_openMBomPopup(objId) {
var popup_width = 1000;
var popup_height = 700;
var url = "/productionplanning/mBomViewPopup.do?objId=" + objId;
fn_centerPopup(popup_width, popup_height, url, 'mbomViewPopup');
}
// M-BOM 편집 팝업 (왼쪽 트리 + 중앙 버튼 + 오른쪽 E-BOM)
function fn_openMBomFormPopup(objId) {
var popup_width = 1800;
var popup_height = 800;
var popup_height = 900;
var url = "/productionplanning/mBomFormPopup.do?objId=" + objId;
fn_centerPopup(popup_width, popup_height, url, 'mbomPopup');
fn_centerPopup(popup_width, popup_height, url, 'mbomFormPopup');
}
// BOM 복사 팝업
@@ -335,22 +355,10 @@ function fn_openBomCopyPopup() {
// BOM 복사 팝업 창 열기
function fn_openBomCopyPopupWindow(objId) {
var popup_width = 1400;
var popup_height = 800;
var url = "/partMng/setBomCopyFormPopup.do?objId=" + objId;
var left = (screen.width - popup_width) / 2;
var top = (screen.height - popup_height) / 2;
var popup = window.open(url, "bomCopyPopup", "width=" + popup_width + ",height=" + popup_height + ",left=" + left + ",top=" + top + ",scrollbars=yes,resizable=yes");
// 팝업이 닫힐 때 그리드 새로고침
var checkPopup = setInterval(function() {
if(popup.closed) {
clearInterval(checkPopup);
console.log("BOM 복사 팝업 닫힘 - 그리드 새로고침");
fn_search();
}
}, 500);
var popup_width = 1800;
var popup_height = 900;
var url = "/partMng/structureBomCopyFormPopup.do?objId=" + objId;
fn_centerPopup(popup_width, popup_height, url, 'bomCopyPopup');
}
</script>

View File

@@ -0,0 +1,146 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<script>
// confirm/alert 헬퍼 함수
function showConfirm(options) {
if(typeof options === 'string') {
alert(options);
return Promise.resolve({isConfirmed: true});
}
var message = '';
if(options.title) message += options.title + '\n\n';
if(options.html) {
// HTML 태그 제거
message += options.html.replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '');
} else if(options.text) {
message += options.text;
}
if(options.showCancelButton !== false && (options.icon === 'warning' || options.icon === 'question')) {
var result = confirm(message);
return Promise.resolve({isConfirmed: result});
} else {
alert(message);
return Promise.resolve({isConfirmed: true});
}
}
$(function(){
$('.select2').select2();
// E-BOM 할당 버튼 (변경)
$("#moveChange").click(function(){
showConfirm({
title: 'E-BOM 할당',
text: 'E-BOM을 선택하여 이 프로젝트에 할당하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '할당',
cancelButtonText: '취소'
}).then(result => {
if (result.isConfirmed) {
fn_assignEbom();
}
});
});
// << 버튼 (현재는 사용 안 함)
$("#moveLeft").click(function(){
showConfirm("이 기능은 현재 사용할 수 없습니다.");
});
// >> 버튼 (현재는 사용 안 함)
$("#moveRight").click(function(){
showConfirm("이 기능은 현재 사용할 수 없습니다.");
});
});
// E-BOM 할당 함수
function fn_assignEbom(){
// 오른쪽 프레임에서 선택된 E-BOM 가져오기
var rightFrame = parent.frames['rightFrame'];
var rightSelectedRows = rightFrame.getSelectedRows ? rightFrame.getSelectedRows() : [];
if(rightSelectedRows.length === 0){
showConfirm("할당할 E-BOM을 선택해주세요.");
return false;
}
if(rightSelectedRows.length > 1){
showConfirm("한 번에 1개의 E-BOM만 할당할 수 있습니다.");
return false;
}
var rowData = rightSelectedRows[0].getData();
var bomReportObjid = rowData.OBJID;
$.ajax({
url: "/productionplanning/assignEbomToMbom.do",
method: 'post',
data: {
"projectMgmtObjid": $("#objId").val(),
"bomReportObjid": bomReportObjid
},
dataType: 'json',
success: function(data) {
if(data.result){
showConfirm({
title: '할당 완료',
text: 'E-BOM이 성공적으로 할당되었습니다.',
icon: 'success'
});
// 왼쪽 프레임 새로고침
parent.frames['leftFrame'].location.reload();
// 부모 창(M-BOM 목록) 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
} else {
showConfirm({
title: '할당 실패',
text: 'E-BOM 할당 중 오류가 발생했습니다.',
icon: 'error'
});
}
},
error: function(jqxhr, status, error){
showConfirm({
title: '오류',
text: 'E-BOM 할당 중 오류가 발생했습니다.',
icon: 'error'
});
}
});
}
</script>
</head>
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
<div id="structurePopupBtnW" style="padding-top:20px;">
<input type="button" value="변경" class="plm_btns" id="moveChange">
<br>
<input type="button" value="<<" class="plm_btns" id="moveLeft" style="margin-left:0px;">
<br>
<input type="button" value=">>" class="plm_btns" id="moveRight" style="margin-left:0px;">
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,7 @@
<%String objId = com.pms.common.utils.CommonUtils.checkNull(request.getParameter("objId"));%>
<frameset cols="*, 100px, *" border="0" noresize>
<frame src="/productionplanning/mBomPopupLeft.do?objId=<%=objId%>" name="leftFrame">
<frame src="/productionplanning/mBomPopupCenter.do?objId=<%=objId%>" name="centerFrame">
<frame src="/productionplanning/mBomPopupRight.do?objId=<%=objId%>" name="rightFrame">
</frameset><noframes></noframes>

View File

@@ -0,0 +1,14 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%
java.util.Map map = (java.util.HashMap)request.getAttribute("info");
if(map == null || map.isEmpty()) {
response.sendRedirect("/common/error500.do");
return;
}
String objId = com.pms.common.utils.CommonUtils.checkNull(map.get("OBJID"));
%>
<frameset rows="100px, *" border="0" noresize>
<frame src="/productionplanning/mBomHeaderPopup.do?objId=<%=objId%>">
<frame src="/productionplanning/mBomBottomPopupFS.do?objId=<%=objId%>">
</frameset><noframes></noframes>

View File

@@ -0,0 +1,231 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
<style>
::-webkit-scrollbar {
width: 10px;
height: 15px;
}
#mBomTableWrap {
width: 99%;
margin: 10px auto;
}
#mBomName {
margin-bottom: 10px;
font-weight: bold;
font-size: 16px;
}
/* Tabulator 커스텀 스타일 */
.tabulator-row.level-1 { background-color: #fde9d9 !important; }
.tabulator-row.level-2 { background-color: #daeef3 !important; }
.tabulator-row.level-3 { background-color: #e4dfec !important; }
.tabulator-row.level-4 { background-color: #ebf1de !important; }
.tabulator-row.level-5 { background-color: #f2f2f2 !important; }
.tabulator-row.level-6 { background-color: #f2dcdb !important; }
.tabulator-row.level-7 { background-color: #eeece1 !important; }
.tabulator-row.level-8 { background-color: #dce6f1 !important; }
.tabulator-row.level-9 { background-color: #FFFFEB !important; }
.tabulator-row.level-10 { background-color: #ffffff !important; }
</style>
<script>
var _tabulGrid;
$(function(){
// Tabulator 초기화
fn_initGrid();
});
// Tabulator 그리드 초기화
function fn_initGrid() {
var maxLevel = ${empty MAXLEV ? 1 : MAXLEV};
// 컬럼 정의
var columns = [];
// 수준 컬럼 그룹
var levelColumns = [];
for(var i = 1; i <= maxLevel; i++) {
levelColumns.push({
headerHozAlign: 'center',
hozAlign: 'center',
width: 30,
title: i,
field: 'LEVEL_' + i,
formatter: function(cell) {
return cell.getValue() === '*' ? '*' : '';
}
});
}
columns.push({
title: '수준',
headerHozAlign: 'center',
columns: levelColumns
});
// 나머지 컬럼 추가
columns.push(
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '품번',
field: 'PART_NO'
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 250,
title: '품명',
field: 'PART_NAME'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 80,
title: '수량',
field: 'QTY_TEMP'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '항목 수량',
field: 'ITEM_QTY'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 80,
title: 'Rev',
field: 'REVISION'
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '규격',
field: 'SPEC'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '제품구분',
field: 'PRODUCT_NAME'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '상태',
field: 'STATUS_NAME'
}
);
// Tabulator 생성
_tabulGrid = new Tabulator("#mBomTableWrap", {
layout: "fitColumns",
height: "calc(100vh - 150px)",
columns: columns,
data: ${bomTreeListJson},
rowFormatter: function(row) {
var data = row.getData();
var level = data.LEVEL || 1;
row.getElement().classList.add('level-' + level);
}
});
}
// 필터 적용 함수
function fn_applyFilter() {
var partNo = $("#filterPartNo").val().trim();
var partName = $("#filterPartName").val().trim();
if(!partNo && !partName) {
_tabulGrid.clearFilter();
return;
}
_tabulGrid.setFilter([
[
{field: "PART_NO", type: "like", value: partNo},
{field: "PART_NAME", type: "like", value: partName}
]
]);
}
// 필터 초기화 함수
function fn_resetFilter() {
$("#filterPartNo").val("");
$("#filterPartName").val("");
_tabulGrid.clearFilter();
}
// M-BOM 조회 (헤더 프레임에서 호출)
function fn_searchMbom(searchParams) {
$.ajax({
url: "/productionplanning/getMbomList.do",
method: 'post',
data: searchParams,
dataType: 'json',
success: function(data) {
if(data && data.list) {
_tabulGrid.setData(data.list);
} else {
_tabulGrid.setData([]);
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 조회 오류:", error);
_tabulGrid.setData([]);
}
});
}
// M-BOM 트리 데이터 수집 (저장용)
function getMbomTreeData() {
var allData = _tabulGrid.getData();
// 데이터 구조 변환 (필요한 필드만 추출)
var mbomData = allData.map(function(row) {
return {
PART_NO: row.PART_NO,
PART_NAME: row.PART_NAME,
QTY: row.QTY_TEMP || row.ITEM_QTY,
LEVEL: row.LEVEL,
REVISION: row.REVISION,
SPEC: row.SPEC,
PRODUCT_NAME: row.PRODUCT_NAME,
STATUS_NAME: row.STATUS_NAME,
OBJID: row.OBJID
};
});
return mbomData;
}
</script>
</head>
<body>
<div id="mBomName">
<c:choose>
<c:when test="${not empty ebomInfo}">
E-BOM: ${ebomInfo.PART_NO} - ${ebomInfo.PART_NAME}
</c:when>
<c:otherwise>
할당된 E-BOM이 없습니다.
</c:otherwise>
</c:choose>
</div>
<div id="mBomTableWrap"></div>
</body>
</html>

View File

@@ -0,0 +1,161 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
margin: 0;
padding: 10px;
height: 100%;
overflow: hidden;
}
#plmSearchZon {
margin-bottom: 10px;
}
#plmSearchZon table {
width: 100%;
}
#plmSearchZon td {
padding: 2px 5px;
}
#plmSearchZon td.label,
#plmSearchZon td:first-child {
text-align: right;
padding-right: 5px;
}
#ebomTable {
height: calc(100% - 80px);
}
</style>
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
</head>
<body class="backcolor">
<form name="form1" id="form1" action="" method="post">
<div id="plmSearchZon">
<table>
<tr>
<td><label for="search_part_no">품번</label></td>
<td>
<input type="text" name="search_part_no" id="search_part_no" value="">
</td>
<td class="label"><label for="search_part_name">품명</label></td>
<td>
<input type="text" name="search_part_name" id="search_part_name" value="">
</td>
</tr>
<tr>
<td><label for="search_material">재료</label></td>
<td>
<input type="text" name="search_material" id="search_material" value="">
</td>
<td class="label"><label for="search_supplier">공급업체</label></td>
<td>
<input type="text" name="search_supplier" id="search_supplier" value="">
</td>
<td>
<input type="button" value="조회" class="plm_btns" id="btnSearch">
</td>
</tr>
</table>
</div>
<div id="ebomTable"></div>
</form>
<script>
var ebomTable;
$(document).ready(function(){
initEbomTable();
fn_searchEbom();
// 조회 버튼 클릭 이벤트
$("#btnSearch").click(function(){
fn_searchEbom();
});
// 엔터키 이벤트
$("#search_part_no, #search_part_name, #search_material, #search_supplier").on('keypress', function(e){
if(e.keyCode == 13 || e.which == 13){
e.preventDefault();
fn_searchEbom();
}
});
});
// Tabulator 초기화
function initEbomTable() {
ebomTable = new Tabulator("#ebomTable", {
layout: "fitColumns",
height: "100%",
selectable: true,
columns: [
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", vertAlign:"middle", headerSort:false, width: 40},
{title: "품번", field: "PART_NO", widthGrow: 1.5, vertAlign:"middle"},
{title: "품명", field: "PART_NAME", widthGrow: 2, vertAlign:"middle"},
{title: "재료", field: "MATERIAL", widthGrow: 1.2, vertAlign:"middle"},
{title: "공급업체", field: "SUPPLIER", widthGrow: 1.5, vertAlign:"middle"}
],
placeholder: "검색 결과가 없습니다."
});
}
// E-BOM 검색
function fn_searchEbom() {
var partNo = $("#search_part_no").val().trim();
var partName = $("#search_part_name").val().trim();
var material = $("#search_material").val().trim();
var supplier = $("#search_supplier").val().trim();
$.ajax({
url: "/productionplanning/getEbomList.do",
method: 'post',
data: {
"search_part_no": partNo,
"search_part_name": partName,
"search_material": material,
"search_supplier": supplier
},
dataType: 'json',
success: function(data) {
if(data && data.list) {
// 품번과 품명이 비어있지 않은 데이터만 필터링
var filteredList = data.list.filter(function(item) {
return item.PART_NO && item.PART_NO.trim() !== '' &&
item.PART_NAME && item.PART_NAME.trim() !== '';
});
ebomTable.setData(filteredList);
} else {
ebomTable.setData([]);
}
},
error: function(jqxhr, status, error){
console.error("E-BOM 조회 오류:", error);
ebomTable.setData([]);
}
});
}
// 선택된 행 가져오기 (중앙 프레임에서 호출)
function getSelectedRows() {
return ebomTable.getSelectedRows();
}
// 선택 해제
function clearSelection() {
ebomTable.deselectRow();
}
</script>
</body>
</html>

View File

@@ -0,0 +1,140 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
margin: 0;
padding: 0;
overflow: visible;
height: auto;
}
.plm_menu_name {
margin: 5px 15px 0 15px !important;
}
.plm_menu_name h2 {
margin: 5px 0 !important;
padding: 5px 0;
}
#plmSearchZon {
margin-bottom: 0 !important;
padding-bottom: 5px !important;
overflow: visible !important;
position: relative;
z-index: 1000;
}
.select2-container {
z-index: 9999 !important;
}
.select2-dropdown {
z-index: 9999 !important;
}
</style>
<script>
$(document).ready(function(){
// 엔터키 이벤트 바인딩
$("#filterPartNo, #filterPartName").on('keypress', function(e){
if(e.keyCode == 13 || e.which == 13){
e.preventDefault();
fn_applyFilter();
}
});
// 페이지 로드 시 품번/품명이 있으면 자동으로 필터 적용
var partNo = $("#filterPartNo").val().trim();
var partName = $("#filterPartName").val().trim();
if(partNo || partName) {
setTimeout(function() {
fn_applyFilter();
}, 500);
}
});
// 필터 적용 함수
function fn_applyFilter() {
var partNo = $("#filterPartNo").val().trim();
var partName = $("#filterPartName").val().trim();
try {
// 프레임 구조: parent(headerFs) -> frames[1](bottomFs) -> frames[0](leftFrame)
var leftFrame = parent.frames[1].frames['leftFrame'];
if(leftFrame && leftFrame.fn_applyFilter) {
leftFrame.$("#filterPartNo").val(partNo);
leftFrame.$("#filterPartName").val(partName);
leftFrame.fn_applyFilter();
} else {
console.error("leftFrame not found or fn_applyFilter not available");
}
} catch(e) {
console.error("Error accessing leftFrame:", e);
alert("필터를 적용할 수 없습니다. 프레임 구조를 확인해주세요.");
}
}
// 필터 초기화 함수
function fn_resetFilter() {
$("#filterPartNo").val("");
$("#filterPartName").val("");
try {
var leftFrame = parent.frames[1].frames['leftFrame'];
if(leftFrame && leftFrame.fn_resetFilter) {
leftFrame.fn_resetFilter();
}
} catch(e) {
console.error("Error accessing leftFrame:", e);
}
}
</script>
</head>
<body class="backcolor">
<form name="form1" id="form1" action="" method="post">
<div class="plm_menu_name">
<h2>
<span>M-BOM 확인</span>
<c:if test="${not empty projectNo}">
<span style="font-size: 14px; font-weight: normal; color: #666; margin-left: 20px;">
프로젝트번호: <strong style="color: #333;">${projectNo}</strong>
<c:if test="${not empty partNo}">
/ 품번: <strong style="color: #333;">${partNo}</strong>
</c:if>
<c:if test="${not empty partName}">
/ 품명: <strong style="color: #333;">${partName}</strong>
</c:if>
</span>
</c:if>
</h2>
</div>
<!-- 검색 필터 영역 -->
<div id="plmSearchZon">
<table>
<tr>
<td class="label"><label for="filterPartNo">품번</label></td>
<td>
<input type="text" id="filterPartNo" name="filterPartNo" value="${partNo}">
</td>
<td class="label"><label for="filterPartName">품명</label></td>
<td>
<input type="text" id="filterPartName" name="filterPartName" value="${partName}">
</td>
<td>
<button type="button" class="plm_btns" onclick="fn_applyFilter()">검색</button>
<button type="button" class="plm_btns" onclick="fn_resetFilter()">초기화</button>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,183 @@
<%@ 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" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
}
.header {
padding: 15px 20px;
background: #f5f5f5;
border-bottom: 2px solid #ddd;
}
.header h3 {
margin: 0 0 10px 0;
color: #333;
}
.info-table {
width: 100%;
border-collapse: collapse;
}
.info-table td {
padding: 5px 10px;
}
.info-table label {
font-weight: bold;
margin-right: 10px;
}
.content {
flex: 1;
padding: 20px;
overflow: auto;
}
.tree-container {
border: 1px solid #ddd;
padding: 10px;
background: white;
min-height: 400px;
}
.tree-node {
padding: 5px 0;
cursor: default;
}
.tree-node-content {
padding: 5px 10px;
display: inline-block;
}
.tree-node-content:hover {
background: #f0f0f0;
}
.tree-children {
margin-left: 30px;
}
.footer {
padding: 15px 20px;
background: #f5f5f5;
border-top: 1px solid #ddd;
text-align: center;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/themes/default/style.min.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jstree/3.3.12/jstree.min.js"></script>
</head>
<body>
<div class="container">
<div class="header">
<h3>M-BOM 조회</h3>
<table class="info-table">
<tr>
<td>
<label>프로젝트 번호:</label>
<span id="projectNo">-</span>
</td>
<td>
<label>품번:</label>
<span id="partNo">-</span>
</td>
<td>
<label>품명:</label>
<span id="partName">-</span>
</td>
</tr>
</table>
</div>
<div class="content">
<div id="mbomTree" class="tree-container"></div>
</div>
<div class="footer">
<input type="button" value="닫기" class="plm_btns" onclick="window.close();">
</div>
</div>
<script>
var objId = "${param.objId}";
$(document).ready(function(){
loadProjectInfo();
loadMBomTree();
});
// 프로젝트 정보 로드
function loadProjectInfo() {
$.ajax({
url: "/productionplanning/getMBomHeaderInfo.do",
method: 'post',
data: { objId: objId },
dataType: 'json',
success: function(data) {
if(data && data.info) {
$("#projectNo").text(data.info.PROJECT_NO || '-');
$("#partNo").text(data.info.PART_NO || '-');
$("#partName").text(data.info.PART_NAME || '-');
}
},
error: function(jqxhr, status, error){
console.error("프로젝트 정보 로드 오류:", error);
}
});
}
// M-BOM 트리 로드
function loadMBomTree() {
$.ajax({
url: "/productionplanning/getMBomTree.do",
method: 'post',
data: { objId: objId },
dataType: 'json',
success: function(data) {
if(data && data.tree) {
initTree(data.tree);
} else {
$("#mbomTree").html('<p style="padding: 20px; text-align: center; color: #999;">저장된 M-BOM이 없습니다.</p>');
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 트리 로드 오류:", error);
$("#mbomTree").html('<p style="padding: 20px; text-align: center; color: #f00;">데이터 로드 중 오류가 발생했습니다.</p>');
}
});
}
// jsTree 초기화
function initTree(treeData) {
$('#mbomTree').jstree({
'core': {
'data': treeData,
'check_callback': false,
'themes': {
'responsive': true
}
},
'plugins': ['types'],
'types': {
'default': {
'icon': 'fa fa-folder'
}
}
});
// 트리 로드 완료 후 모두 펼치기
$('#mbomTree').on('ready.jstree', function() {
$('#mbomTree').jstree('open_all');
});
}
</script>
</body>
</html>