Files
wace_plm/WebContent/WEB-INF/view/productionplanning/mBomHeaderPopup.jsp

1204 lines
40 KiB
Plaintext

<%@ 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();
// 페이지 로드 시 프로젝트 정보로 검색 필드 자동 입력
<c:if test="${not empty info}">
$("#search_part_no").val("${info.PART_NO}");
$("#search_part_name").val("${info.PART_NAME}");
$("#search_quantity").val("${info.QUANTITY}"); // 프로젝트 수주수량
// 저장된 M-BOM 품번 조회 및 표시
var projectObjId = "${info.OBJID}";
$.ajax({
url: "/productionplanning/getLatestMbomByProjectId.do",
type: "POST",
data: { projectObjId: projectObjId },
dataType: "json",
async: false,
success: function(response) {
if(response && response.MBOM_NO) {
console.log("저장된 M-BOM 발견:", response);
$("#search_mbom_part_no").val(response.MBOM_NO);
$("#search_save_date").val(response.REGDATE);
} else {
console.log("저장된 M-BOM 없음");
$("#search_mbom_part_no").val("");
$("#search_save_date").val("");
}
},
error: function(xhr, status, error) {
console.error("M-BOM 조회 오류:", error);
}
});
// 할당된 BOM 정보 확인 및 자동 로드
var sourceBomType = "${info.SOURCE_BOM_TYPE}";
var sourceEbomObjId = "${info.SOURCE_EBOM_OBJID}";
var sourceMbomObjId = "${info.SOURCE_MBOM_OBJID}";
console.log("할당된 BOM 정보:", {
sourceBomType: sourceBomType,
sourceEbomObjId: sourceEbomObjId,
sourceMbomObjId: sourceMbomObjId
});
// Controller에서 이미 데이터를 로드하여 JSP에 전달하므로 자동 조회 불필요
// setTimeout(function() {
// fn_searchMbom();
// }, 500);
</c:if>
//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);
}
});
});
// 이력보기 버튼 클릭
$("#btnHistory").click(function(){
fn_showHistory();
});
// 저장 버튼 클릭
$("#btnSave").click(function(){
fn_saveMbom();
});
// 닫기 버튼 클릭
$("#btnClose").click(function(){
window.close();
});
// 일괄 적용 버튼 클릭
$("#btnApplyBulkDeadline").click(function(){
fn_applyBulkDeadline();
});
});
//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); */
}
});
}
// 가공납기/연삭납기 일괄 적용
function fn_applyBulkDeadline() {
var processingDeadline = $("#bulk_processing_deadline").val();
var grindingDeadline = $("#bulk_grinding_deadline").val();
if(!processingDeadline && !grindingDeadline) {
alert("가공납기 또는 연삭납기를 입력해주세요.");
return;
}
// 확인 메시지
var message = "선택한 날짜를 전체 항목에 일괄 적용하시겠습니까?\n\n";
if(processingDeadline) message += "가공납기: " + processingDeadline + "\n";
if(grindingDeadline) message += "연삭납기: " + grindingDeadline;
if(!confirm(message)) {
return;
}
// 왼쪽 프레임의 함수 호출
var bottomFrame = parent.frames[1];
var leftFrame = bottomFrame ? bottomFrame.frames['leftFrame'] : null;
if(!leftFrame || !leftFrame.applyBulkDeadline) {
alert("M-BOM 화면을 찾을 수 없습니다.");
return;
}
// 일괄 적용 실행
var updatedCount = leftFrame.applyBulkDeadline(processingDeadline, grindingDeadline);
if(updatedCount > 0) {
alert("일괄 적용이 완료되었습니다. (" + updatedCount + "개 항목)");
} else {
alert("적용할 데이터가 없습니다.");
}
}
// 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();
// 할당된 BOM 정보 사용 (우선순위: SOURCE_EBOM_OBJID > SOURCE_MBOM_OBJID > 기존 BOM_REPORT_OBJID)
var sourceBomType = "${info.SOURCE_BOM_TYPE}";
var sourceEbomObjId = "${info.SOURCE_EBOM_OBJID}";
var sourceMbomObjId = "${info.SOURCE_MBOM_OBJID}";
var bomReportObjId = "";
if(sourceBomType === "EBOM" && sourceEbomObjId) {
bomReportObjId = sourceEbomObjId;
} else if(sourceBomType === "MBOM" && sourceMbomObjId) {
bomReportObjId = sourceMbomObjId;
} else {
bomReportObjId = "${info.BOM_REPORT_OBJID}"; // 기존 방식 (하위 호환)
}
console.log("fn_searchMbom 호출:", {
sourceBomType: sourceBomType,
bomReportObjId: bomReportObjId,
partNo: partNo,
partName: partName,
mbomPartNo: mbomPartNo,
saveDate: saveDate
});
// 왼쪽 프레임의 조회 함수 호출
var bottomFrame = parent.frames[1]; // 하단 프레임셋
var leftFrame = bottomFrame ? bottomFrame.frames['leftFrame'] : null;
if(leftFrame && leftFrame.fn_searchMbom) {
leftFrame.fn_searchMbom({
bomReportObjId: bomReportObjId,
sourceBomType: sourceBomType,
partNo: partNo,
partName: partName,
mbomPartNo: mbomPartNo,
saveDate: saveDate
});
} else {
console.error("왼쪽 프레임 또는 fn_searchMbom 함수를 찾을 수 없습니다.");
}
}
// M-BOM 저장
function fn_saveMbom() {
console.log("fn_saveMbom 호출됨");
// 프레임 구조: parent(최상위) -> parent.frames[1](하단) -> parent.frames[1].frames['leftFrame']
var bottomFrame = parent.frames[1]; // 하단 프레임 (mBomPopupFs.jsp)
console.log("bottomFrame:", bottomFrame);
if(!bottomFrame) {
alert("하단 프레임을 찾을 수 없습니다.");
return;
}
var leftFrame = bottomFrame.frames['leftFrame']; // 왼쪽 프레임 (mBomPopupLeft.jsp)
console.log("leftFrame:", leftFrame);
if(!leftFrame) {
alert("M-BOM 데이터를 가져올 수 없습니다.");
return;
}
// 트리 데이터 수집
var mbomData = leftFrame.getMbomTreeData ? leftFrame.getMbomTreeData() : null;
console.log("mbomData:", mbomData);
console.log("mbomData length:", mbomData ? mbomData.length : 0);
if(!mbomData || mbomData.length === 0) {
alert("저장할 M-BOM 데이터가 없습니다.");
return;
}
// 저장 확인
if(!confirm("M-BOM을 저장하시겠습니까?")) {
return;
}
var projectObjId = "${info.OBJID}";
// 기존 M-BOM 존재 여부 확인 (동기 처리)
var existingMbom = null;
$.ajax({
url: "/productionplanning/getLatestMbomByProjectId.do",
type: "POST",
data: { projectObjId: projectObjId },
dataType: "json",
async: false,
success: function(response) {
if(response && response.OBJID) {
existingMbom = response;
console.log("기존 M-BOM 발견:", existingMbom);
}
}
});
var saveData = {
projectObjId: projectObjId, // PROJECT_MGMT의 OBJID
mbomData: mbomData,
partNo: $("#search_part_no").val().trim(),
partName: $("#search_part_name").val().trim(),
mbomPartNo: existingMbom ? existingMbom.MBOM_NO : "", // 기존 M-BOM 품번 사용
isUpdate: existingMbom !== null // 기존 M-BOM이 있으면 업데이트
};
console.log("저장할 데이터:", saveData);
console.log("isUpdate:", saveData.isUpdate);
// 저장 API 호출
$.ajax({
url: "/productionplanning/saveMbom.do",
method: 'post',
data: JSON.stringify(saveData),
contentType: 'application/json',
dataType: 'json',
success: function(data) {
console.log("저장 응답:", data);
if(data && data.result === "success") {
alert("M-BOM이 저장되었습니다.");
// 부모 창 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
// 현재 창 닫기
window.close();
} else {
alert("M-BOM 저장에 실패했습니다: " + (data.message || ""));
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 저장 오류:", error);
console.error("응답:", jqxhr.responseText);
alert("M-BOM 저장 중 오류가 발생했습니다.");
}
});
}
// M-BOM 이력보기
function fn_showHistory() {
var projectObjId = "${info.OBJID}";
if(!projectObjId) {
alert("프로젝트 정보가 없습니다.");
return;
}
// M-BOM 이력 조회 (프로젝트 기준)
$.ajax({
url: "/productionplanning/getMbomHistory.do",
method: 'post',
data: {
projectObjId: projectObjId
},
dataType: 'json',
success: function(data) {
if(data && data.historyList && data.historyList.length > 0) {
// 이력 상세 분석 및 HTML 생성
var historyHtml = generateHistoryHtml(data.historyList);
// 새 창으로 이력 표시
var historyWindow = window.open("", "mbomHistory", "width=1400,height=800,scrollbars=yes,resizable=yes");
historyWindow.document.write("<html><head><title>M-BOM 변경 이력</title>");
historyWindow.document.write("<style>");
historyWindow.document.write("body { font-family: Arial, sans-serif; margin: 20px; }");
historyWindow.document.write("h2 { text-align: center; color: #333; }");
historyWindow.document.write("table { width: 100%; border-collapse: collapse; margin-bottom: 30px; }");
historyWindow.document.write("th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }");
historyWindow.document.write("th { background-color: #4CAF50; color: white; font-weight: bold; }");
historyWindow.document.write(".history-header { background-color: #f0f0f0; font-weight: bold; }");
historyWindow.document.write(".change-add { background-color: #e7f4e7; }");
historyWindow.document.write(".change-delete { background-color: #ffe7e7; }");
historyWindow.document.write(".change-modify { background-color: #fff4e6; }");
historyWindow.document.write(".change-bom { background-color: #e6f3ff; }");
historyWindow.document.write(".field-changed { color: #d9534f; font-weight: bold; }");
historyWindow.document.write(".section-title { background-color: #5bc0de; color: white; font-weight: bold; text-align: center; }");
historyWindow.document.write("</style>");
historyWindow.document.write("</head><body>");
historyWindow.document.write("<h2>M-BOM 변경 이력</h2>");
historyWindow.document.write(historyHtml);
historyWindow.document.write("</body></html>");
historyWindow.document.close();
} else {
alert("이력이 없습니다.");
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 이력 조회 오류:", error);
alert("M-BOM 이력 조회 중 오류가 발생했습니다.");
}
});
}
// 이력 HTML 생성 함수
function generateHistoryHtml(historyList) {
var html = "";
for(var i = 0; i < historyList.length; i++) {
var history = historyList[i];
var changeType = history.CHANGE_TYPE || "UNKNOWN";
var changeDate = history.CHANGE_DATE || history.REGDATE || "";
var changeUser = history.CHANGE_USER || "";
// 이력 헤더
html += "<div style='margin-bottom: 40px;'>";
html += "<h3 style='background-color: #333; color: white; padding: 10px;'>";
html += "이력 #" + (i + 1) + " - " + changeType + " (" + changeDate + " / " + changeUser + ")";
html += "</h3>";
try {
var beforeData = history.BEFORE_DATA ? JSON.parse(history.BEFORE_DATA) : null;
var afterData = history.AFTER_DATA ? JSON.parse(history.AFTER_DATA) : null;
if(changeType === "CREATE") {
// 생성: afterData만 표시
html += generateCreateHistoryTable(afterData);
} else if(changeType === "UPDATE") {
// 수정: 변경 사항 비교
html += generateUpdateHistoryTable(beforeData, afterData);
}
} catch(e) {
console.error("JSON 파싱 오류:", e);
html += "<p style='color: red;'>데이터 파싱 오류</p>";
}
html += "</div>";
}
return html;
}
// 생성 이력 테이블 생성
function generateCreateHistoryTable(afterData) {
var html = "<table>";
html += "<tr class='section-title'><td colspan='4'>생성된 M-BOM 정보</td></tr>";
html += "<tr><th>항목</th><th>값</th><th>항목</th><th>값</th></tr>";
// 헤더 정보
html += "<tr>";
html += "<td>M-BOM 품번</td><td>" + (afterData.mbomNo || afterData.MBOM_NO || "") + "</td>";
html += "<td>품번</td><td>" + (afterData.partNo || afterData.PART_NO || "") + "</td>";
html += "</tr>";
html += "<tr>";
html += "<td>품명</td><td>" + (afterData.partName || afterData.PART_NAME || "") + "</td>";
html += "<td>기준 BOM 유형</td><td>" + (afterData.sourceBomType || afterData.SOURCE_BOM_TYPE || "") + "</td>";
html += "</tr>";
// BOM 상세 항목
var mbomData = afterData.mbomData || [];
if(mbomData.length > 0) {
html += "<tr class='section-title'><td colspan='4'>생성된 BOM 항목 (" + mbomData.length + "개)</td></tr>";
html += "<tr class='change-add'><th>품번</th><th>품명</th><th>수량</th><th>레벨</th></tr>";
for(var i = 0; i < mbomData.length; i++) {
var item = mbomData[i];
html += "<tr class='change-add'>";
html += "<td>" + (item.partNo || "") + "</td>";
html += "<td>" + (item.partName || "") + "</td>";
html += "<td>" + (item.qty || "") + "</td>";
html += "<td>" + (item.level || "") + "</td>";
html += "</tr>";
}
}
html += "</table>";
return html;
}
// 수정 이력 테이블 생성 (새 형식)
function generateUpdateHistoryTable(beforeData, afterData) {
var html = "";
// beforeData는 이제 변경된 항목 배열
var changedItems = beforeData;
if(!Array.isArray(changedItems) || changedItems.length === 0) {
return "<p>변경 사항이 없습니다.</p>";
}
html += "<table>";
html += "<tr class='section-title'><td colspan='4'>변경된 항목 (" + changedItems.length + "개)</td></tr>";
html += "<tr><th>변경유형</th><th>품번</th><th>품명</th><th>변경 내용</th></tr>";
for(var i = 0; i < changedItems.length; i++) {
var item = changedItems[i];
var changeType = item.changeType;
if(changeType === "ADD") {
// 추가된 항목
var afterData = item.afterData || {};
html += "<tr class='change-add'>";
html += "<td>추가</td>";
html += "<td>" + (afterData.partNo || "") + "</td>";
html += "<td>" + (afterData.partName || "") + "</td>";
html += "<td>새 항목 추가</td>";
html += "</tr>";
} else if(changeType === "DELETE") {
// 삭제된 항목
var beforeDataItem = item.beforeData || {};
html += "<tr class='change-delete'>";
html += "<td>삭제</td>";
html += "<td>" + (beforeDataItem.PART_NO || "") + "</td>";
html += "<td>" + (beforeDataItem.PART_NAME || "") + "</td>";
html += "<td>항목 삭제</td>";
html += "</tr>";
} else if(changeType === "MODIFY") {
// 수정된 항목
html += "<tr class='change-modify'>";
html += "<td>수정</td>";
html += "<td>" + (item.partNo || "") + "</td>";
html += "<td>" + (item.partName || "") + "</td>";
html += "<td>";
var fieldChanges = item.fieldChanges || [];
for(var j = 0; j < fieldChanges.length; j++) {
var fc = fieldChanges[j];
var fieldNameKr = getFieldNameKorean(fc.field);
html += "<strong>" + fieldNameKr + ":</strong> ";
html += "<span class='before-value'>" + (fc.before || "(없음)") + "</span> → ";
html += "<span class='after-value'>" + (fc.after || "(없음)") + "</span>";
if(j < fieldChanges.length - 1) html += "<br>";
}
html += "</td>";
html += "</tr>";
}
}
html += "</table>";
return html;
}
// 필드명 한글 변환
function getFieldNameKorean(field) {
var fieldMap = {
"QTY": "수량",
"SUPPLY_TYPE": "자급/사급",
"RAW_MATERIAL": "소재",
"RAW_MATERIAL_SIZE": "사이즈",
"RAW_MATERIAL_PART_NO": "소재품번",
"PROCESSING_VENDOR": "가공업체",
"PROCESSING_DEADLINE": "가공납기",
"GRINDING_DEADLINE": "연삭납기",
"REQUIRED_QTY": "소재소요량",
"ORDER_QTY": "소재발주수량",
"PRODUCTION_QTY": "제작수량",
"REMARK": "비고"
};
return fieldMap[field] || field;
}
// 기존 함수들은 사용하지 않지만 남겨둠
function oldGenerateUpdateHistoryTable(beforeData, afterData) {
var html = "";
// 1. 헤더 정보 변경 확인
var headerChanges = compareHeaderData(beforeData, afterData);
if(headerChanges.length > 0) {
html += "<table>";
html += "<tr class='section-title'><td colspan='3'>헤더 정보 변경</td></tr>";
html += "<tr class='change-bom'><th>항목</th><th>변경 전</th><th>변경 후</th></tr>";
for(var i = 0; i < headerChanges.length; i++) {
var change = headerChanges[i];
html += "<tr class='change-bom'>";
html += "<td>" + change.field + "</td>";
html += "<td>" + change.before + "</td>";
html += "<td class='field-changed'>" + change.after + "</td>";
html += "</tr>";
}
html += "</table><br>";
}
// 2. BOM 항목 변경 분석
var beforeItems = beforeData.mbomData || [];
var afterItems = afterData.mbomData || [];
var changes = compareBomItems(beforeItems, afterItems);
// Excel 스타일 통합 테이블 생성
if(false && changes.added.length > 0 || changes.deleted.length > 0 || changes.modified.length > 0) {
html += "<table style='font-size: 11px;'>";
html += "<tr class='section-title'>";
html += "<th>구분</th>";
html += "<th>품번</th>";
html += "<th>품명</th>";
html += "<th>수량</th>";
html += "<th>자급/사급</th>";
html += "<th>소재</th>";
html += "<th>사이즈</th>";
html += "<th>소재품번</th>";
html += "<th>소재소요량</th>";
html += "<th>가공업체</th>";
html += "<th>가공납기</th>";
html += "<th>연삭납기</th>";
html += "<th>소재발주수량</th>";
html += "<th>제작수량</th>";
html += "<th>비고</th>";
html += "</tr>";
// 추가된 항목
for(var i = 0; i < changes.added.length; i++) {
var item = changes.added[i];
html += "<tr class='change-add'>";
html += "<td>추가</td>";
html += "<td>" + (item.partNo || "") + "</td>";
html += "<td>" + (item.partName || "") + "</td>";
html += "<td>" + (item.qty || "") + "</td>";
html += "<td>" + (item.supplyType || "") + "</td>";
html += "<td>" + (item.rawMaterial || "") + "</td>";
html += "<td>" + (item.rawMaterialSize || "") + "</td>";
html += "<td>" + (item.rawMaterialPartNo || "") + "</td>";
html += "<td>" + (item.requiredQty || "") + "</td>";
html += "<td>" + (item.processingVendor || "") + "</td>";
html += "<td>" + (item.processingDeadline || "") + "</td>";
html += "<td>" + (item.grindingDeadline || "") + "</td>";
html += "<td>" + (item.orderQty || "") + "</td>";
html += "<td>" + (item.productionQty || "") + "</td>";
html += "<td>" + (item.remark || "") + "</td>";
html += "</tr>";
}
// 삭제된 항목
for(var i = 0; i < changes.deleted.length; i++) {
var item = changes.deleted[i];
html += "<tr class='change-delete'>";
html += "<td>삭제</td>";
html += "<td>" + (item.partNo || "") + "</td>";
html += "<td>" + (item.partName || "") + "</td>";
html += "<td>" + (item.qty || "") + "</td>";
html += "<td>" + (item.supplyType || "") + "</td>";
html += "<td>" + (item.rawMaterial || "") + "</td>";
html += "<td>" + (item.rawMaterialSize || "") + "</td>";
html += "<td>" + (item.rawMaterialPartNo || "") + "</td>";
html += "<td>" + (item.requiredQty || "") + "</td>";
html += "<td>" + (item.processingVendor || "") + "</td>";
html += "<td>" + (item.processingDeadline || "") + "</td>";
html += "<td>" + (item.grindingDeadline || "") + "</td>";
html += "<td>" + (item.orderQty || "") + "</td>";
html += "<td>" + (item.productionQty || "") + "</td>";
html += "<td>" + (item.remark || "") + "</td>";
html += "</tr>";
}
// 수정된 항목 (변경된 필드만 강조)
for(var i = 0; i < changes.modified.length; i++) {
var mod = changes.modified[i];
var changedFields = {};
for(var j = 0; j < mod.changes.length; j++) {
changedFields[mod.changes[j].fieldKey] = true;
}
html += "<tr class='change-modify'>";
html += "<td>수정</td>";
html += "<td>" + (mod.partNo || "") + "</td>";
html += "<td>" + (mod.partName || "") + "</td>";
html += "<td" + (changedFields['qty'] ? " class='field-changed'" : "") + ">" + (mod.after.qty || "") + "</td>";
html += "<td" + (changedFields['supplyType'] ? " class='field-changed'" : "") + ">" + (mod.after.supplyType || "") + "</td>";
html += "<td" + (changedFields['rawMaterial'] ? " class='field-changed'" : "") + ">" + (mod.after.rawMaterial || "") + "</td>";
html += "<td" + (changedFields['rawMaterialSize'] ? " class='field-changed'" : "") + ">" + (mod.after.rawMaterialSize || "") + "</td>";
html += "<td" + (changedFields['rawMaterialPartNo'] ? " class='field-changed'" : "") + ">" + (mod.after.rawMaterialPartNo || "") + "</td>";
html += "<td" + (changedFields['requiredQty'] ? " class='field-changed'" : "") + ">" + (mod.after.requiredQty || "") + "</td>";
html += "<td" + (changedFields['processingVendor'] ? " class='field-changed'" : "") + ">" + (mod.after.processingVendor || "") + "</td>";
html += "<td" + (changedFields['processingDeadline'] ? " class='field-changed'" : "") + ">" + (mod.after.processingDeadline || "") + "</td>";
html += "<td" + (changedFields['grindingDeadline'] ? " class='field-changed'" : "") + ">" + (mod.after.grindingDeadline || "") + "</td>";
html += "<td" + (changedFields['orderQty'] ? " class='field-changed'" : "") + ">" + (mod.after.orderQty || "") + "</td>";
html += "<td" + (changedFields['productionQty'] ? " class='field-changed'" : "") + ">" + (mod.after.productionQty || "") + "</td>";
html += "<td" + (changedFields['remark'] ? " class='field-changed'" : "") + ">" + (mod.after.remark || "") + "</td>";
html += "</tr>";
}
html += "</table>";
}
return html;
}
// 헤더 데이터 비교
function compareHeaderData(before, after) {
var changes = [];
var fieldsToCheck = {
'SOURCE_BOM_TYPE': '기준 BOM 유형',
'SOURCE_EBOM_OBJID': '기준 E-BOM',
'SOURCE_MBOM_OBJID': '기준 M-BOM',
'PART_NO': '품번',
'PART_NAME': '품명',
'MBOM_STATUS': 'M-BOM 상태'
};
for(var key in fieldsToCheck) {
var beforeVal = before[key] || before[key.toLowerCase()] || "";
var afterVal = after[key] || after[key.toLowerCase()] || "";
if(beforeVal != afterVal) {
changes.push({
field: fieldsToCheck[key],
before: beforeVal,
after: afterVal
});
}
}
return changes;
}
// BOM 항목 비교
function compareBomItems(beforeItems, afterItems) {
var added = [];
var deleted = [];
var modified = [];
// childObjid를 키로 사용하여 매핑
var beforeMap = {};
var afterMap = {};
for(var i = 0; i < beforeItems.length; i++) {
var item = beforeItems[i];
var key = item.childObjid || item.objid;
if(key) beforeMap[key] = item;
}
for(var i = 0; i < afterItems.length; i++) {
var item = afterItems[i];
var key = item.childObjid || item.objid;
if(key) afterMap[key] = item;
}
// 추가된 항목 찾기
for(var key in afterMap) {
if(!beforeMap[key]) {
added.push(afterMap[key]);
}
}
// 삭제된 항목 찾기
for(var key in beforeMap) {
if(!afterMap[key]) {
deleted.push(beforeMap[key]);
}
}
// 수정된 항목 찾기
for(var key in afterMap) {
if(beforeMap[key]) {
var itemChanges = compareItemFields(beforeMap[key], afterMap[key]);
if(itemChanges.length > 0) {
modified.push({
partNo: afterMap[key].partNo,
partName: afterMap[key].partName,
before: beforeMap[key],
after: afterMap[key],
changes: itemChanges
});
}
}
}
return {
added: added,
deleted: deleted,
modified: modified
};
}
// 항목 필드 비교
function compareItemFields(before, after) {
var changes = [];
var fieldsToCheck = {
'qty': '수량',
'supplyType': '자급/사급',
'rawMaterial': '소재',
'rawMaterialSize': '사이즈',
'rawMaterialPartNo': '소재품번',
'processingVendor': '가공업체',
'processingDeadline': '가공납기',
'grindingDeadline': '연삭납기',
'requiredQty': '소재소요량',
'orderQty': '소재발주수량',
'productionQty': '제작수량',
'remark': '비고'
};
for(var key in fieldsToCheck) {
var beforeVal = before[key] || "";
var afterVal = after[key] || "";
// 값 비교 (타입 변환 고려)
if(String(beforeVal) != String(afterVal)) {
changes.push({
field: fieldsToCheck[key],
fieldKey: key, // 필드 키 추가 (CSS 클래스 적용용)
before: beforeVal,
after: afterVal
});
}
}
return changes;
}
</script>
</head>
<body>
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="objId" id="objId" value="${param.objId}" />
<input type="hidden" name="search_quantity" id="search_quantity" value="" />
<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="btnHistory">
<input type="button" value="저장" class="plm_btns" id="btnSave">
<input type="button" value="닫기" class="plm_btns" id="btnClose">
</td>
</tr>
<tr>
<td><label for="bulk_processing_deadline">가공납기 일괄</label></td>
<td>
<input type="date" name="bulk_processing_deadline" id="bulk_processing_deadline" value="">
</td>
<td class="label"><label for="bulk_grinding_deadline">연삭납기 일괄</label></td>
<td>
<input type="date" name="bulk_grinding_deadline" id="bulk_grinding_deadline" value="">
</td>
<td>
<input type="button" value="일괄 적용" class="plm_btns" id="btnApplyBulkDeadline">
</td>
</tr>
</table>
</div>
</form>
</body>
</html>