Merge remote-tracking branch 'origin/main' into V2025111703
This commit is contained in:
@@ -248,7 +248,7 @@ $(document).ready(function(){
|
||||
grid = $("#expenseDetailGrid").jqGrid({
|
||||
url: ""
|
||||
,datatype: "local"
|
||||
,colNames: ["상태","모품번","품번","품명","수량","항목수량","재료","열처리경도","열처리방법","표면처리","공급업체","범주이름"]
|
||||
,colNames: ["상태","모품번","품번","품명","수량","항목수량","재료","열처리경도","열처리방법","표면처리","메이커","범주이름"]
|
||||
,colModel: [
|
||||
{name:"NOTE",index:"NOTE", width: 200, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
@@ -320,7 +320,7 @@ $(document).ready(function(){
|
||||
}
|
||||
}
|
||||
}
|
||||
// 공급업체 - MAKER 컬럼으로 변경, 일반 텍스트 입력 (2025-10-29)
|
||||
// 메이커 - MAKER 컬럼으로 변경, 일반 텍스트 입력 (2025-10-29)
|
||||
,{name:"MAKER",index:"MAKER", width: 120, align:"center", hidden: false, sortable:false, editable:true
|
||||
,editoptions:{
|
||||
dataInit : function(e){
|
||||
|
||||
@@ -134,30 +134,30 @@ var selectedBomType = null; // 'EBOM' 또는 'MBOM'
|
||||
var bomGridData = []; // BOM 그리드 데이터
|
||||
|
||||
$(function(){
|
||||
// 페이지 로드 시 URL 파라미터 또는 프로젝트 정보에서 품번/품명 자동 입력
|
||||
var urlPartNo = "${param.partNo}";
|
||||
var urlPartName = "${param.partName}";
|
||||
var productCode = "${projectInfo.PRODUCT}";
|
||||
// Select2 초기화
|
||||
$('.select2').select2();
|
||||
|
||||
if(urlPartNo && urlPartNo !== "") {
|
||||
console.log("URL 파라미터에서 품번/품명 설정 중...");
|
||||
$("#COPY_PART_NO").val(decodeURIComponent(urlPartNo));
|
||||
$("#COPY_PART_NAME").val(decodeURIComponent(urlPartName));
|
||||
}
|
||||
<c:if test="${not empty projectInfo}">
|
||||
else {
|
||||
console.log("projectInfo가 있습니다. 품번/품명 설정 중...");
|
||||
$("#COPY_PART_NO").val("${projectInfo.PART_NO}");
|
||||
$("#COPY_PART_NAME").val("${projectInfo.PART_NAME}");
|
||||
}
|
||||
|
||||
// Machine이 아닌 경우, 동일 품번의 기존 M-BOM 확인 및 자동 로드
|
||||
var isMachine = productCode && (productCode === '0001807' || productCode.toUpperCase().indexOf('MACHINE') >= 0);
|
||||
if(!isMachine && "${projectInfo.PART_NO}" !== "") {
|
||||
console.log("Machine이 아닌 제품입니다. 기존 M-BOM 확인 중...");
|
||||
fn_checkExistingMbom("${projectInfo.PART_NO}");
|
||||
}
|
||||
</c:if>
|
||||
// 페이지 로드 시 품번/품명 자동 입력
|
||||
// 우선순위: urlParamInfo(POST 파라미터) > projectInfo(DB 조회)
|
||||
<c:choose>
|
||||
<c:when test="${not empty urlParamInfo}">
|
||||
// POST 파라미터로 전달된 품번/품명 사용
|
||||
$("#COPY_PART_NO").val("${urlParamInfo.PART_NO}");
|
||||
$("#COPY_PART_NAME").val("${urlParamInfo.PART_NAME}");
|
||||
</c:when>
|
||||
<c:when test="${not empty projectInfo}">
|
||||
// DB에서 조회한 프로젝트 정보 사용
|
||||
$("#COPY_PART_NO").val("${projectInfo.PART_NO}");
|
||||
$("#COPY_PART_NAME").val("${projectInfo.PART_NAME}");
|
||||
|
||||
// Machine이 아닌 경우, 동일 품번의 기존 M-BOM 확인 및 자동 로드
|
||||
var productCode = "${projectInfo.PRODUCT}";
|
||||
var isMachine = productCode && (productCode === '0001807' || productCode.toUpperCase().indexOf('MACHINE') >= 0);
|
||||
if(!isMachine && "${projectInfo.PART_NO}" !== "") {
|
||||
fn_checkExistingMbom("${projectInfo.PART_NO}");
|
||||
}
|
||||
</c:when>
|
||||
</c:choose>
|
||||
|
||||
// 담기 버튼 - 선택한 BOM을 복사 대상으로 설정
|
||||
$("#btnAddItem").click(function(){
|
||||
@@ -187,28 +187,40 @@ $(function(){
|
||||
fn_saveBomCopy();
|
||||
});
|
||||
|
||||
// E-BOM 선택 버튼
|
||||
$("#btnSelectEbom").click(function(){
|
||||
var ebomPartNo = $("#EBOM_PART_NO").val().trim();
|
||||
if(!ebomPartNo) {
|
||||
Swal.fire('E-BOM 품번을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// E-BOM 조회 후 미리보기 로드
|
||||
fn_loadBomPreview(ebomPartNo, 'EBOM');
|
||||
// E-BOM 셀렉트박스 변경 이벤트
|
||||
$("#EBOM_SELECT").change(function(){
|
||||
var selectedObjId = $(this).val();
|
||||
if(!selectedObjId) {
|
||||
// 선택 해제 시 M-BOM 셀렉트박스 활성화 및 그리드 초기화
|
||||
$("#MBOM_SELECT").prop("disabled", false);
|
||||
_tabulGrid.clearData();
|
||||
$("#bomPartName").text("");
|
||||
return;
|
||||
}
|
||||
|
||||
// E-BOM 선택 시 M-BOM 셀렉트박스 비활성화
|
||||
$("#MBOM_SELECT").val("").prop("disabled", true);
|
||||
|
||||
// 선택된 E-BOM으로 미리보기 로드
|
||||
fn_loadBomPreviewByObjId(selectedObjId, 'EBOM');
|
||||
});
|
||||
|
||||
// M-BOM 선택 버튼
|
||||
$("#btnSelectMbom").click(function(){
|
||||
var mbomPartNo = $("#MBOM_PART_NO").val().trim();
|
||||
if(!mbomPartNo) {
|
||||
Swal.fire('M-BOM 품번을 입력해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
// M-BOM 조회 후 미리보기 로드
|
||||
fn_loadBomPreview(mbomPartNo, 'MBOM');
|
||||
// M-BOM 셀렉트박스 변경 이벤트
|
||||
$("#MBOM_SELECT").change(function(){
|
||||
var selectedObjId = $(this).val();
|
||||
if(!selectedObjId) {
|
||||
// 선택 해제 시 E-BOM 셀렉트박스 활성화 및 그리드 초기화
|
||||
$("#EBOM_SELECT").prop("disabled", false);
|
||||
_tabulGrid.clearData();
|
||||
$("#bomPartName").text("");
|
||||
return;
|
||||
}
|
||||
|
||||
// M-BOM 선택 시 E-BOM 셀렉트박스 비활성화
|
||||
$("#EBOM_SELECT").val("").prop("disabled", true);
|
||||
|
||||
// 선택된 M-BOM으로 미리보기 로드
|
||||
fn_loadBomPreviewByObjId(selectedObjId, 'MBOM');
|
||||
});
|
||||
|
||||
// Excel 다운로드 버튼
|
||||
@@ -269,7 +281,7 @@ function fn_checkExistingMbom(partNo) {
|
||||
});
|
||||
}
|
||||
|
||||
// BOM 미리보기 로드
|
||||
// BOM 미리보기 로드 (품번으로)
|
||||
function fn_loadBomPreview(partNo, bomType) {
|
||||
console.log("fn_loadBomPreview 호출:", partNo, bomType);
|
||||
|
||||
@@ -301,6 +313,23 @@ function fn_loadBomPreview(partNo, bomType) {
|
||||
});
|
||||
}
|
||||
|
||||
// BOM 미리보기 로드 (OBJID로 직접)
|
||||
function fn_loadBomPreviewByObjId(objId, bomType) {
|
||||
console.log("fn_loadBomPreviewByObjId 호출:", objId, bomType);
|
||||
|
||||
// 전역 변수에 저장
|
||||
selectedBomObjId = objId;
|
||||
selectedBomType = bomType;
|
||||
|
||||
// 셀렉트박스에서 선택된 텍스트를 품명으로 표시
|
||||
var selectBox = bomType === 'EBOM' ? $("#EBOM_SELECT") : $("#MBOM_SELECT");
|
||||
var selectedText = selectBox.find("option:selected").text();
|
||||
$("#bomPartName").text(selectedText);
|
||||
|
||||
// BOM 트리 데이터 로드
|
||||
fn_loadBomTree(objId);
|
||||
}
|
||||
|
||||
// BOM 트리 데이터 로드
|
||||
function fn_loadBomTree(bomObjId) {
|
||||
console.log("fn_loadBomTree 호출:", bomObjId);
|
||||
@@ -375,6 +404,7 @@ function fn_initGrid(data, maxLevel) {
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
headerSort: false,
|
||||
title: '선택',
|
||||
field: 'RADIO',
|
||||
formatter: function(cell) {
|
||||
@@ -397,6 +427,7 @@ function fn_initGrid(data, maxLevel) {
|
||||
levelColumns.push({
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
headerSort: false,
|
||||
width: 30,
|
||||
title: i,
|
||||
field: 'LEVEL_' + i,
|
||||
@@ -417,20 +448,23 @@ function fn_initGrid(data, maxLevel) {
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
headerSort: false,
|
||||
width: 220,
|
||||
title: '품번',
|
||||
field: 'PART_NO'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 200,
|
||||
headerSort: false,
|
||||
// width: 200,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
headerSort: false,
|
||||
width: 60,
|
||||
title: '수량',
|
||||
field: 'QTY_TEMP'
|
||||
@@ -438,13 +472,15 @@ function fn_initGrid(data, maxLevel) {
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
headerSort: false,
|
||||
width: 80,
|
||||
title: '항목 수량',
|
||||
field: 'ITEM_QTY'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
headerSort: false,
|
||||
width: 60,
|
||||
title: '3D',
|
||||
field: 'CU01_CNT',
|
||||
@@ -457,6 +493,7 @@ function fn_initGrid(data, maxLevel) {
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
headerSort: false,
|
||||
width: 60,
|
||||
title: '2D',
|
||||
field: 'CU02_CNT',
|
||||
@@ -468,7 +505,8 @@ function fn_initGrid(data, maxLevel) {
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
headerSort: false,
|
||||
width: 60,
|
||||
title: 'PDF',
|
||||
field: 'CU03_CNT',
|
||||
@@ -481,16 +519,50 @@ function fn_initGrid(data, maxLevel) {
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 100,
|
||||
headerSort: false,
|
||||
width: 130,
|
||||
title: '재료',
|
||||
field: 'MATERIAL'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
headerSort: false,
|
||||
width: 120,
|
||||
title: '열처리경도',
|
||||
field: 'HEAT_TREATMENT_HARDNESS'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
headerSort: false,
|
||||
width: 130,
|
||||
title: '열처리방법',
|
||||
field: 'HEAT_TREATMENT_METHOD'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
headerSort: false,
|
||||
width: 130,
|
||||
title: '표면처리',
|
||||
field: 'SURFACE_TREATMENT'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
headerSort: false,
|
||||
width: 120,
|
||||
title: '메이커',
|
||||
field: 'MAKER'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
headerSort: false,
|
||||
width: 100,
|
||||
title: '범주이름',
|
||||
field: 'CATEGORY_NAME'
|
||||
}
|
||||
);
|
||||
|
||||
@@ -515,13 +587,18 @@ function fn_initGrid(data, maxLevel) {
|
||||
});
|
||||
}
|
||||
|
||||
// BOM 복사 저장
|
||||
// BOM 복사 저장 - PROJECT_MGMT에 할당 정보만 저장
|
||||
function fn_saveBomCopy() {
|
||||
var copyPartNo = $("#COPY_PART_NO").val().trim();
|
||||
var copyPartName = $("#COPY_PART_NAME").val().trim();
|
||||
var targetObjId = $("#TARGET_OBJID").val();
|
||||
|
||||
// 유효성 검사
|
||||
if(!targetObjId) {
|
||||
Swal.fire('프로젝트가 선택되지 않았습니다.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!copyPartNo || !copyPartName) {
|
||||
Swal.fire('품번과 품명을 입력해주세요.');
|
||||
return;
|
||||
@@ -538,50 +615,35 @@ function fn_saveBomCopy() {
|
||||
}
|
||||
|
||||
// 확인 메시지
|
||||
var confirmMessage = targetObjId
|
||||
? '선택한 ' + selectedBomType + '을(를) M-BOM으로 복사하시겠습니까?\n기존 M-BOM이 있다면 초기화됩니다.'
|
||||
: '선택한 ' + selectedBomType + '을(를) 새로운 품번(' + copyPartNo + ')으로 복사하시겠습니까?';
|
||||
var confirmMessage = '선택한 ' + selectedBomType + '을(를) M-BOM 기준으로 할당하시겠습니까?\n' +
|
||||
'실제 M-BOM 생성은 M-BOM 상세 팝업에서 저장 시 이루어집니다.';
|
||||
|
||||
Swal.fire({
|
||||
title: 'BOM 복사',
|
||||
title: 'BOM 할당',
|
||||
text: confirmMessage,
|
||||
icon: 'question',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: '복사',
|
||||
confirmButtonText: '할당',
|
||||
cancelButtonText: '취소'
|
||||
}).then((result) => {
|
||||
if(result.isConfirmed) {
|
||||
// 저장 처리
|
||||
// 할당 처리
|
||||
Swal.fire({
|
||||
title: '저장 중...',
|
||||
text: 'BOM을 복사하고 있습니다.',
|
||||
title: '할당 중...',
|
||||
text: 'BOM 할당 정보를 저장하고 있습니다.',
|
||||
allowOutsideClick: false,
|
||||
didOpen: () => {
|
||||
Swal.showLoading();
|
||||
}
|
||||
});
|
||||
|
||||
// 제품구분 가져오기
|
||||
var productCode = "${projectInfo.PRODUCT}";
|
||||
|
||||
// 기존 M-BOM 품번 가져오기 (M-BOM 선택 시 사용)
|
||||
var existingMbomPartNo = "";
|
||||
if(selectedBomType === 'MBOM') {
|
||||
existingMbomPartNo = $("#MBOM_PART_NO").val().trim();
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "/partMng/saveBomCopy.do",
|
||||
url: "/productionplanning/saveBomAssignment.do",
|
||||
type: "POST",
|
||||
data: JSON.stringify({
|
||||
targetObjId: targetObjId, // M-BOM 관리에서 선택한 프로젝트 OBJID
|
||||
sourceBomObjId: selectedBomObjId,
|
||||
sourceBomType: selectedBomType,
|
||||
targetPartNo: copyPartNo,
|
||||
targetPartName: copyPartName,
|
||||
productCode: productCode, // 제품구분 추가
|
||||
existingMbomPartNo: existingMbomPartNo, // 기존 M-BOM 품번 추가
|
||||
bomData: bomGridData
|
||||
projectObjId: targetObjId,
|
||||
sourceBomType: selectedBomType, // 'EBOM' or 'MBOM'
|
||||
sourceBomObjId: selectedBomObjId
|
||||
}),
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
@@ -589,30 +651,22 @@ function fn_saveBomCopy() {
|
||||
Swal.close();
|
||||
if(response && response.result === 'success') {
|
||||
Swal.fire({
|
||||
title: '저장 완료',
|
||||
text: 'M-BOM이 성공적으로 생성되었습니다.',
|
||||
title: '할당 완료',
|
||||
text: 'BOM 할당 정보가 저장되었습니다.\nM-BOM 셀을 클릭하여 실제 M-BOM을 생성하세요.',
|
||||
icon: 'success'
|
||||
}).then(() => {
|
||||
// 부모 창(M-BOM 관리)의 검색 함수만 호출하여 그리드 업데이트
|
||||
// 부모 창(M-BOM 관리)의 검색 함수 호출하여 그리드 업데이트
|
||||
if(window.opener && !window.opener.closed) {
|
||||
// 부모 창의 검색 조건 유지하면서 그리드만 새로고침
|
||||
if(typeof window.opener.fn_search === 'function') {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
// M-BOM 품번과 저장일을 부모 창 검색 조건에 설정
|
||||
// if(response.mbomPartNo) {
|
||||
// window.opener.$("#search_mbom_part_no").val(response.mbomPartNo);
|
||||
// }
|
||||
// if(response.saveDate) {
|
||||
// window.opener.$("#search_save_date").val(response.saveDate);
|
||||
// }
|
||||
}
|
||||
window.close();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: '저장 실패',
|
||||
text: response.message || 'BOM 복사 중 오류가 발생했습니다.',
|
||||
title: '할당 실패',
|
||||
text: response.message || 'BOM 할당 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
@@ -621,7 +675,7 @@ function fn_saveBomCopy() {
|
||||
Swal.close();
|
||||
console.error('Save error:', error);
|
||||
Swal.fire({
|
||||
title: '저장 실패',
|
||||
title: '할당 실패',
|
||||
text: '서버 오류가 발생했습니다: ' + error,
|
||||
icon: 'error'
|
||||
});
|
||||
@@ -653,55 +707,55 @@ function fn_excel() {
|
||||
<div class="top-input-section">
|
||||
<table class="pmsPopuptable" style="margin-bottom: 10px;">
|
||||
<colgroup>
|
||||
<col width="10%">
|
||||
<col width="25%">
|
||||
<col width="10%">
|
||||
<col width="25%">
|
||||
<col width="30%">
|
||||
<col width="10%">
|
||||
<col width="20%">
|
||||
<col width="10%">
|
||||
<col width="20%">
|
||||
<col width="30%">
|
||||
<col width="10%">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td class="input_title"><label for="COPY_PART_NO">품번</label></td>
|
||||
<td>
|
||||
<input type="text" id="COPY_PART_NO" name="COPY_PART_NO" style="width: 100%;">
|
||||
</td>
|
||||
</td>
|
||||
<td class="input_title"><label for="COPY_PART_NAME">품명</label></td>
|
||||
<td>
|
||||
<input type="text" id="COPY_PART_NAME" name="COPY_PART_NAME" style="width: 100%;">
|
||||
</td>
|
||||
</td>
|
||||
<td></td>
|
||||
<td style="text-align: center;">
|
||||
<input type="button" value="담기" class="plm_btns" id="btnAddItem">
|
||||
<!-- <input type="button" value="담기" class="plm_btns" id="btnAddItem"> -->
|
||||
<input type="button" class="plm_btns" value="닫기" onclick="window.close();" >
|
||||
<input type="button" value="저장" class="plm_btns" id="btnSaveItem">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="pmsPopuptable">
|
||||
<colgroup>
|
||||
<col width="10%">
|
||||
<col width="25%">
|
||||
<col width="10%">
|
||||
<col width="25%">
|
||||
<col width="30%">
|
||||
<col width="90%">
|
||||
</colgroup>
|
||||
<tr>
|
||||
<td class="input_title"><label for="EBOM_PART_NO">E-BOM 품번</label></td>
|
||||
<td colspan="3">
|
||||
<input type="text" id="EBOM_PART_NO" name="EBOM_PART_NO" style="width: 100%;">
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<input type="button" value="E-BOM 선택" class="plm_btns" id="btnSelectEbom">
|
||||
</td>
|
||||
</tr>
|
||||
<td class="input_title"><label for="EBOM_SELECT">E-BOM 선택</label></td>
|
||||
<td>
|
||||
<select id="EBOM_SELECT" name="EBOM_SELECT" style="width: 100%;" class="select2">
|
||||
<option value="">선택</option>
|
||||
${code_map.ebom_list}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="input_title"><label for="MBOM_PART_NO">M-BOM 품번</label></td>
|
||||
<td colspan="3">
|
||||
<input type="text" id="MBOM_PART_NO" name="MBOM_PART_NO" style="width: 100%;">
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
<input type="button" value="M-BOM 선택" class="plm_btns" id="btnSelectMbom">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<td class="input_title"><label for="MBOM_SELECT">M-BOM 선택</label></td>
|
||||
<td>
|
||||
<select id="MBOM_SELECT" name="MBOM_SELECT" style="width: 100%;" class="select2">
|
||||
<option value="">선택</option>
|
||||
${code_map.mbom_list}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 중간: BOM 미리보기 그리드 -->
|
||||
|
||||
@@ -278,7 +278,7 @@ function fn_initGrid() {
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: '공급업체',
|
||||
title: '메이커',
|
||||
field: 'MAKER',
|
||||
headerSort: false
|
||||
},
|
||||
|
||||
@@ -151,7 +151,7 @@ function fn_initRightGrid(){
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 120,
|
||||
title: 'MAKER',
|
||||
title: '메이커',
|
||||
field: 'MAKER'
|
||||
}
|
||||
];
|
||||
@@ -301,7 +301,7 @@ function clearSelection() {
|
||||
<input type="text" name="search_spec" id="search_spec" value="">
|
||||
</td>
|
||||
|
||||
<td class="label"><label for="search_maker">MAKER</label></td>
|
||||
<td class="label"><label for="search_maker">메이커</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_maker" id="search_maker" value="">
|
||||
</td>
|
||||
|
||||
@@ -54,13 +54,47 @@ $(function(){
|
||||
<c:if test="${not empty info}">
|
||||
$("#search_part_no").val("${info.PART_NO}");
|
||||
$("#search_part_name").val("${info.PART_NAME}");
|
||||
$("#search_mbom_part_no").val("${info.MBOM_PART_NO}"); // M-BOM 품번 (자동 생성된 값)
|
||||
$("#search_save_date").val("${info.MBOM_REGDATE}");
|
||||
$("#search_quantity").val("${info.QUANTITY}"); // 프로젝트 수주수량
|
||||
|
||||
// 자동으로 M-BOM 조회
|
||||
setTimeout(function() {
|
||||
fn_searchMbom();
|
||||
}, 500);
|
||||
// 저장된 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 연결
|
||||
@@ -268,6 +302,11 @@ $(function(){
|
||||
window.close();
|
||||
});
|
||||
|
||||
// 일괄 적용 버튼 클릭
|
||||
$("#btnApplyBulkDeadline").click(function(){
|
||||
fn_applyBulkDeadline();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
//1레벨에 같은 Part No가 등록되어있는지 확인.
|
||||
@@ -461,15 +500,67 @@ function fn_changeRelatePartInfo(objId,rightObjId,leftObjId,leftPartNoQty,leftPa
|
||||
});
|
||||
}
|
||||
|
||||
// 가공납기/연삭납기 일괄 적용
|
||||
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();
|
||||
var bomReportObjId = "${info.BOM_REPORT_OBJID}"; // M-BOM의 BOM_REPORT_OBJID
|
||||
|
||||
// 할당된 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,
|
||||
@@ -478,15 +569,20 @@ function fn_searchMbom() {
|
||||
});
|
||||
|
||||
// 왼쪽 프레임의 조회 함수 호출
|
||||
var leftFrame = parent.frames['leftFrame'];
|
||||
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 함수를 찾을 수 없습니다.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,8 +590,16 @@ function fn_searchMbom() {
|
||||
function fn_saveMbom() {
|
||||
console.log("fn_saveMbom 호출됨");
|
||||
|
||||
// 왼쪽 프레임에서 M-BOM 트리 데이터 가져오기
|
||||
var leftFrame = parent.frames['leftFrame'];
|
||||
// 프레임 구조: 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) {
|
||||
@@ -518,15 +622,35 @@ function fn_saveMbom() {
|
||||
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: $("#search_mbom_part_no").val().trim(),
|
||||
isUpdate: $("#search_mbom_part_no").val().trim() !== ""
|
||||
mbomPartNo: existingMbom ? existingMbom.MBOM_NO : "", // 기존 M-BOM 품번 사용
|
||||
isUpdate: existingMbom !== null // 기존 M-BOM이 있으면 업데이트
|
||||
};
|
||||
|
||||
console.log("저장할 데이터:", saveData);
|
||||
console.log("isUpdate:", saveData.isUpdate);
|
||||
|
||||
// 저장 API 호출
|
||||
$.ajax({
|
||||
@@ -540,13 +664,13 @@ function fn_saveMbom() {
|
||||
if(data && data.result === "success") {
|
||||
alert("M-BOM이 저장되었습니다.");
|
||||
|
||||
// 조회 새로고침
|
||||
fn_searchMbom();
|
||||
|
||||
// 부모 창 새로고침
|
||||
if(window.opener && window.opener.fn_search) {
|
||||
window.opener.fn_search();
|
||||
}
|
||||
|
||||
// 현재 창 닫기
|
||||
window.close();
|
||||
} else {
|
||||
alert("M-BOM 저장에 실패했습니다: " + (data.message || ""));
|
||||
}
|
||||
@@ -561,50 +685,45 @@ function fn_saveMbom() {
|
||||
|
||||
// M-BOM 이력보기
|
||||
function fn_showHistory() {
|
||||
var mbomPartNo = $("#search_mbom_part_no").val().trim();
|
||||
var projectObjId = "${info.OBJID}";
|
||||
|
||||
if(!mbomPartNo) {
|
||||
alert("M-BOM 품번이 없습니다. 먼저 M-BOM을 저장해주세요.");
|
||||
if(!projectObjId) {
|
||||
alert("프로젝트 정보가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// M-BOM 이력 조회
|
||||
// M-BOM 이력 조회 (프로젝트 기준)
|
||||
$.ajax({
|
||||
url: "/productionplanning/getMbomHistory.do",
|
||||
method: 'post',
|
||||
data: {
|
||||
mbomPartNo: mbomPartNo
|
||||
projectObjId: projectObjId
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(data) {
|
||||
if(data && data.historyList && data.historyList.length > 0) {
|
||||
// 이력 팝업 표시
|
||||
var historyHtml = "<table border='1' style='width:100%; border-collapse:collapse;'>";
|
||||
historyHtml += "<thead><tr style='background:#f0f0f0;'>";
|
||||
historyHtml += "<th style='padding:8px;'>순번</th>";
|
||||
historyHtml += "<th style='padding:8px;'>M-BOM 품번</th>";
|
||||
historyHtml += "<th style='padding:8px;'>저장일</th>";
|
||||
historyHtml += "<th style='padding:8px;'>수정자</th>";
|
||||
historyHtml += "<th style='padding:8px;'>비고</th>";
|
||||
historyHtml += "</tr></thead><tbody>";
|
||||
|
||||
for(var i = 0; i < data.historyList.length; i++) {
|
||||
var item = data.historyList[i];
|
||||
historyHtml += "<tr>";
|
||||
historyHtml += "<td style='padding:5px; text-align:center;'>" + (i + 1) + "</td>";
|
||||
historyHtml += "<td style='padding:5px;'>" + (item.MBOM_PART_NO || "") + "</td>";
|
||||
historyHtml += "<td style='padding:5px; text-align:center;'>" + (item.REGDATE || "") + "</td>";
|
||||
historyHtml += "<td style='padding:5px; text-align:center;'>" + (item.REGUSER || "") + "</td>";
|
||||
historyHtml += "<td style='padding:5px;'>" + (item.REMARK || "") + "</td>";
|
||||
historyHtml += "</tr>";
|
||||
}
|
||||
|
||||
historyHtml += "</tbody></table>";
|
||||
// 이력 상세 분석 및 HTML 생성
|
||||
var historyHtml = generateHistoryHtml(data.historyList);
|
||||
|
||||
// 새 창으로 이력 표시
|
||||
var historyWindow = window.open("", "mbomHistory", "width=800,height=600,scrollbars=yes,resizable=yes");
|
||||
historyWindow.document.write("<html><head><title>M-BOM 이력</title></head><body>");
|
||||
historyWindow.document.write("<h2 style='text-align:center;'>M-BOM 수정 이력</h2>");
|
||||
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();
|
||||
@@ -618,11 +737,419 @@ function fn_showHistory() {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 이력 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>
|
||||
@@ -654,6 +1181,21 @@ function fn_showHistory() {
|
||||
<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>
|
||||
|
||||
@@ -207,24 +207,9 @@ var columns = [
|
||||
cellClick: function(e, cell) {
|
||||
var rowData = cell.getData();
|
||||
var objid = fnc_checkNull(rowData.OBJID);
|
||||
var mbomStatus = fnc_checkNull(rowData.MBOM_STATUS);
|
||||
|
||||
// 파란색(저장된 M-BOM)일 때만 팝업 열기
|
||||
if(mbomStatus !== '' && mbomStatus !== '0') {
|
||||
// 검색 조건에 해당 행의 데이터 자동 입력 (주석처리)
|
||||
// $("#search_part_no").val(fnc_checkNull(rowData.PART_NO));
|
||||
// $("#search_part_name").val(fnc_checkNull(rowData.PART_NAME));
|
||||
// $("#search_mbom_part_no").val(fnc_checkNull(rowData.MBOM_PART_NO)); // M-BOM 품번 (자동 생성된 값)
|
||||
// $("#search_save_date").val(fnc_checkNull(rowData.MBOM_REGDATE));
|
||||
|
||||
fn_openMBomFormPopup(objid);
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: '알림',
|
||||
text: 'M-BOM이 생성되지 않았습니다.\nBOM 복사 버튼을 통해 먼저 M-BOM을 생성해주세요.',
|
||||
icon: 'info'
|
||||
});
|
||||
}
|
||||
// 할당 정보 확인 후 M-BOM 팝업 열기
|
||||
fn_checkAssignmentAndOpenMbom(objid);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -365,19 +350,72 @@ function fn_openBomCopyPopupWindow(objId) {
|
||||
var selectedRow = _tabulGrid.searchRows("OBJID", "=", objId);
|
||||
if(selectedRow.length > 0) {
|
||||
var rowData = selectedRow[0].getData();
|
||||
var partNo = encodeURIComponent(fnc_checkNull(rowData.PART_NO));
|
||||
var partName = encodeURIComponent(fnc_checkNull(rowData.PART_NAME));
|
||||
var partNo = fnc_checkNull(rowData.PART_NO);
|
||||
var partName = fnc_checkNull(rowData.PART_NAME);
|
||||
|
||||
// hiddenForm을 사용하여 POST 방식으로 팝업 열기 (한글 인코딩 문제 해결)
|
||||
var hiddenForm = document.hiddenForm;
|
||||
var url = "/partMng/structureBomCopyFormPopup.do";
|
||||
var target = "bomCopyPopup";
|
||||
|
||||
hiddenForm.objId.value = objId;
|
||||
hiddenForm.partNo.value = partNo;
|
||||
hiddenForm.partName.value = partName;
|
||||
|
||||
var popup_width = 1800;
|
||||
var popup_height = 900;
|
||||
var url = "/partMng/structureBomCopyFormPopup.do?objId=" + objId + "&partNo=" + partNo + "&partName=" + partName;
|
||||
fn_centerPopup(popup_width, popup_height, url, 'bomCopyPopup');
|
||||
window.open('', target, 'width=' + popup_width + ', height=' + popup_height + ', resizable=yes');
|
||||
hiddenForm.action = url;
|
||||
hiddenForm.target = target;
|
||||
hiddenForm.submit();
|
||||
} else {
|
||||
Swal.fire('선택된 데이터를 찾을 수 없습니다.');
|
||||
}
|
||||
}
|
||||
|
||||
// 할당 정보 확인 후 M-BOM 팝업 열기
|
||||
function fn_checkAssignmentAndOpenMbom(projectObjId) {
|
||||
// PROJECT_MGMT의 BOM 할당 정보 조회
|
||||
$.ajax({
|
||||
url: "/productionplanning/getMbomAssignmentInfo.do",
|
||||
type: "POST",
|
||||
data: { projectObjId: projectObjId },
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
console.log("BOM 할당 정보:", response);
|
||||
|
||||
if(!response || !response.SOURCE_BOM_TYPE) {
|
||||
// 할당 정보가 없는 경우
|
||||
Swal.fire({
|
||||
title: '알림',
|
||||
text: 'BOM 할당 정보가 없습니다.\nBOM 복사 버튼을 통해 먼저 BOM을 할당해주세요.',
|
||||
icon: 'info'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 할당된 BOM 정보가 있는 경우 기존 M-BOM 팝업 열기
|
||||
fn_openMBomFormPopup(projectObjId);
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("BOM 할당 정보 조회 오류:", error);
|
||||
Swal.fire({
|
||||
title: '오류',
|
||||
text: 'BOM 할당 정보를 조회하는 중 오류가 발생했습니다.',
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- hiddenForm: POST 방식 팝업 전송용 -->
|
||||
<form name="hiddenForm" id="hiddenForm" method="post">
|
||||
<input type="hidden" name="objId" id="objId">
|
||||
<input type="hidden" name="partNo" id="partNo">
|
||||
<input type="hidden" name="partName" id="partName">
|
||||
</form>
|
||||
|
||||
<form name="form1" id="form1" method="post">
|
||||
<input type="hidden" name="actionType" id="actionType">
|
||||
<div class="content-box">
|
||||
@@ -388,6 +426,7 @@ function fn_openBomCopyPopupWindow(objId) {
|
||||
</h2>
|
||||
<div class="btnArea">
|
||||
<input type="button" class="plm_btns" value="조회" id="btnSearch">
|
||||
<input type="button" class="plm_btns" value="구매리스트 생성" id="btnCreatePurchaseList">
|
||||
<input type="button" class="plm_btns" value="BOM 복사" id="btnBomCopy">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,9 +6,18 @@ if(map == null || map.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
String objId = com.pms.common.utils.CommonUtils.checkNull(map.get("OBJID"));
|
||||
String quantity = com.pms.common.utils.CommonUtils.checkNull(map.get("QUANTITY"));
|
||||
%>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
// 프로젝트 수주수량을 전역 변수로 설정 (하위 프레임에서 접근 가능)
|
||||
var PROJECT_QUANTITY = <%=quantity.isEmpty() ? "1" : quantity%>;
|
||||
</script>
|
||||
</head>
|
||||
<frameset rows="100px, *" border="0" noresize>
|
||||
<frame src="/productionplanning/mBomHeaderPopup.do?objId=<%=objId%>">
|
||||
<frame src="/productionplanning/mBomBottomPopupFS.do?objId=<%=objId%>">
|
||||
</frameset><noframes></noframes>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -42,8 +42,22 @@ body {
|
||||
</style>
|
||||
<script>
|
||||
var _tabulGrid;
|
||||
// 프로젝트 수주수량 (최상위 프레임에서 가져오기)
|
||||
var projectQuantity = 1; // 기본값
|
||||
|
||||
$(function(){
|
||||
// 최상위 프레임(mBomPopupHeaderFs.jsp)에서 프로젝트 수주수량 가져오기
|
||||
try {
|
||||
if(parent && parent.parent && parent.parent.PROJECT_QUANTITY) {
|
||||
projectQuantity = parseFloat(parent.parent.PROJECT_QUANTITY) || 1;
|
||||
console.log("프로젝트 수주수량:", projectQuantity);
|
||||
} else {
|
||||
console.log("PROJECT_QUANTITY를 찾을 수 없습니다. 기본값 1 사용");
|
||||
}
|
||||
} catch(e) {
|
||||
console.log("프로젝트 수주수량 가져오기 실패:", e);
|
||||
}
|
||||
|
||||
// Tabulator 초기화
|
||||
fn_initGrid();
|
||||
});
|
||||
@@ -59,10 +73,10 @@ function fn_initGrid() {
|
||||
columns.push({
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 60,
|
||||
width: 40,
|
||||
title: '선택',
|
||||
field: 'RADIO',
|
||||
headerSort: false,
|
||||
frozen: true,
|
||||
formatter: function(cell) {
|
||||
var rowData = cell.getData();
|
||||
return '<input type="radio" name="checkedPartNo" value="' + (rowData.CHILD_OBJID || '') + '" ' +
|
||||
@@ -85,7 +99,7 @@ function fn_initGrid() {
|
||||
levelColumns.push({
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 25,
|
||||
width: 10,
|
||||
title: i,
|
||||
field: 'LEVEL_' + i,
|
||||
formatter: function(cell) {
|
||||
@@ -97,6 +111,7 @@ function fn_initGrid() {
|
||||
columns.push({
|
||||
title: '수준',
|
||||
headerHozAlign: 'center',
|
||||
frozen: true,
|
||||
columns: levelColumns
|
||||
});
|
||||
|
||||
@@ -107,14 +122,16 @@ function fn_initGrid() {
|
||||
hozAlign: 'left',
|
||||
widthGrow: 2,
|
||||
title: '품번',
|
||||
field: 'PART_NO'
|
||||
field: 'PART_NO',
|
||||
frozen: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
widthGrow: 3,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
field: 'PART_NAME',
|
||||
frozen: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -122,7 +139,7 @@ function fn_initGrid() {
|
||||
width: 60,
|
||||
title: '수량',
|
||||
field: 'QTY_TEMP',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -130,7 +147,7 @@ function fn_initGrid() {
|
||||
width: 80,
|
||||
title: '항목 수량',
|
||||
field: 'ITEM_QTY',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
/* 주석처리: Rev, 규격, 제품구분, 상태 컬럼
|
||||
{
|
||||
@@ -168,7 +185,7 @@ function fn_initGrid() {
|
||||
width: 60,
|
||||
title: '3D',
|
||||
field: 'CU01_CNT',
|
||||
visible: false,
|
||||
visible: true,
|
||||
formatter: function(cell) {
|
||||
var value = cell.getValue();
|
||||
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
|
||||
@@ -180,7 +197,7 @@ function fn_initGrid() {
|
||||
width: 60,
|
||||
title: '2D',
|
||||
field: 'CU02_CNT',
|
||||
visible: false,
|
||||
visible: true,
|
||||
formatter: function(cell) {
|
||||
var value = cell.getValue();
|
||||
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
|
||||
@@ -192,7 +209,7 @@ function fn_initGrid() {
|
||||
width: 60,
|
||||
title: 'PDF',
|
||||
field: 'CU03_CNT',
|
||||
visible: false,
|
||||
visible: true,
|
||||
formatter: function(cell) {
|
||||
var value = cell.getValue();
|
||||
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
|
||||
@@ -204,7 +221,7 @@ function fn_initGrid() {
|
||||
width: 100,
|
||||
title: '재료',
|
||||
field: 'MATERIAL',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -212,7 +229,7 @@ function fn_initGrid() {
|
||||
width: 120,
|
||||
title: '열처리경도',
|
||||
field: 'HEAT_TREATMENT_HARDNESS',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -220,7 +237,7 @@ function fn_initGrid() {
|
||||
width: 120,
|
||||
title: '열처리방법',
|
||||
field: 'HEAT_TREATMENT_METHOD',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -228,23 +245,23 @@ function fn_initGrid() {
|
||||
width: 100,
|
||||
title: '표면처리',
|
||||
field: 'SURFACE_TREATMENT',
|
||||
visible: false
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '공급업체',
|
||||
field: 'SUPPLIER',
|
||||
visible: false
|
||||
title: '메이커',
|
||||
field: 'MAKER',
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 100,
|
||||
title: '범주이름',
|
||||
field: 'CATEGORY_NAME',
|
||||
visible: false
|
||||
field: 'PART_TYPE_TITLE',
|
||||
visible: true
|
||||
}
|
||||
);
|
||||
|
||||
@@ -257,11 +274,32 @@ function fn_initGrid() {
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '지급/사급',
|
||||
title: '자급/사급',
|
||||
field: 'SUPPLY_TYPE',
|
||||
editor: 'list',
|
||||
editorParams: {
|
||||
values: ['지급', '사급', '자급']
|
||||
values: ['자급', '사급']
|
||||
},
|
||||
cellEdited: function(cell) {
|
||||
var row = cell.getRow();
|
||||
var data = row.getData();
|
||||
// 자급 선택 시 소재 관련 필드 초기화
|
||||
if(data.SUPPLY_TYPE === '자급') {
|
||||
row.update({
|
||||
RAW_MATERIAL: '-',
|
||||
SIZE: '-',
|
||||
RAW_MATERIAL_NO: '-',
|
||||
REQUIRED_QTY: '-'
|
||||
});
|
||||
} else {
|
||||
// 사급 선택 시 초기화
|
||||
row.update({
|
||||
RAW_MATERIAL: '',
|
||||
SIZE: '',
|
||||
RAW_MATERIAL_NO: '',
|
||||
REQUIRED_QTY: ''
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -270,10 +308,17 @@ function fn_initGrid() {
|
||||
width: 100,
|
||||
title: '소재',
|
||||
field: 'RAW_MATERIAL',
|
||||
editor: 'input',
|
||||
editor: 'list',
|
||||
editorParams: {
|
||||
values: ['SM45C', 'STS304', 'STS316', 'AL6061', 'AL7075'] // TODO: 실제 소재 목록으로 교체
|
||||
},
|
||||
editable: function(cell) {
|
||||
// 자급인 경우 입력 불가
|
||||
return cell.getRow().getData().SUPPLY_TYPE !== '자급';
|
||||
return cell.getRow().getData().SUPPLY_TYPE === '사급';
|
||||
},
|
||||
formatter: function(cell) {
|
||||
var data = cell.getRow().getData();
|
||||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||||
return cell.getValue() || '';
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -282,50 +327,89 @@ function fn_initGrid() {
|
||||
width: 100,
|
||||
title: '사이즈',
|
||||
field: 'SIZE',
|
||||
editor: false,
|
||||
editor: 'list',
|
||||
editorParams: {
|
||||
values: ['Φ10', 'Φ20', 'Φ30', '10x10', '20x20'] // TODO: 실제 사이즈 목록으로 교체
|
||||
},
|
||||
editable: function(cell) {
|
||||
return cell.getRow().getData().SUPPLY_TYPE === '사급';
|
||||
},
|
||||
formatter: function(cell) {
|
||||
// 항목수량 × 수주수량 (수정 불가)
|
||||
var data = cell.getRow().getData();
|
||||
var itemQty = data.ITEM_QTY || 0;
|
||||
var orderQty = data.ORDER_QTY || 0;
|
||||
return itemQty * orderQty;
|
||||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||||
return cell.getValue() || '';
|
||||
},
|
||||
cellEdited: function(cell) {
|
||||
// 소재, 사이즈 선택 시 소재품번 자동 생성
|
||||
var row = cell.getRow();
|
||||
var data = row.getData();
|
||||
if(data.RAW_MATERIAL && data.SIZE) {
|
||||
var materialNo = data.RAW_MATERIAL + '-' + data.SIZE;
|
||||
row.update({RAW_MATERIAL_NO: materialNo});
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '소요량',
|
||||
field: 'REQUIRED_QTY',
|
||||
editor: false
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '발주수량',
|
||||
field: 'ORDER_QTY',
|
||||
width: 120,
|
||||
title: '소재품번',
|
||||
field: 'RAW_MATERIAL_NO',
|
||||
editor: false,
|
||||
formatter: function(cell) {
|
||||
// 항목수량 (수정 불가)
|
||||
return cell.getRow().getData().ITEM_QTY || 0;
|
||||
var data = cell.getRow().getData();
|
||||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||||
return cell.getValue() || '';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: "Q'ty",
|
||||
field: 'QTY',
|
||||
hozAlign: 'right',
|
||||
width: 100,
|
||||
title: '소재소요량',
|
||||
field: 'REQUIRED_QTY',
|
||||
editor: 'number',
|
||||
editorParams: {
|
||||
min: 0,
|
||||
step: 1
|
||||
step: 0.01 // 소수 가능
|
||||
},
|
||||
editable: function(cell) {
|
||||
return cell.getRow().getData().SUPPLY_TYPE === '사급';
|
||||
},
|
||||
formatter: function(cell) {
|
||||
var data = cell.getRow().getData();
|
||||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||||
var value = cell.getValue();
|
||||
return value ? Number(value).toLocaleString() : '0';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 120,
|
||||
title: '소재발주수량',
|
||||
field: 'ORDER_QTY',
|
||||
editor: false,
|
||||
formatter: function(cell) {
|
||||
// 항목수량 × 프로젝트 수주수량 (수정 불가)
|
||||
var data = cell.getRow().getData();
|
||||
var itemQty = parseFloat(data.ITEM_QTY) || 0;
|
||||
var orderQty = itemQty * projectQuantity;
|
||||
return orderQty.toLocaleString();
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 80,
|
||||
title: '항목수량',
|
||||
field: 'ITEM_QTY',
|
||||
editor: false,
|
||||
visible: true
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 100,
|
||||
title: '제작수량',
|
||||
field: 'PRODUCTION_QTY',
|
||||
@@ -333,6 +417,16 @@ function fn_initGrid() {
|
||||
editorParams: {
|
||||
min: 0,
|
||||
step: 1
|
||||
},
|
||||
formatter: function(cell) {
|
||||
// 초기값은 소재발주수량 (항목수량 × 프로젝트 수주수량)
|
||||
var value = cell.getValue();
|
||||
if(!value) {
|
||||
var data = cell.getRow().getData();
|
||||
var itemQty = parseFloat(data.ITEM_QTY) || 0;
|
||||
value = itemQty * projectQuantity;
|
||||
}
|
||||
return Number(value).toLocaleString();
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -341,7 +435,10 @@ function fn_initGrid() {
|
||||
width: 150,
|
||||
title: '가공업체',
|
||||
field: 'PROCESSING_VENDOR',
|
||||
editor: 'input'
|
||||
editor: 'list',
|
||||
editorParams: {
|
||||
values: ['업체A', '업체B', '업체C'] // TODO: 실제 가공업체 목록으로 교체
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -358,50 +455,6 @@ function fn_initGrid() {
|
||||
title: '연삭납기',
|
||||
field: 'GRINDING_DEADLINE',
|
||||
editor: 'date'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 구매 컬럼 그룹 (제작수량 기준)
|
||||
columns.push({
|
||||
title: '구매',
|
||||
headerHozAlign: 'center',
|
||||
columns: [
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '소재품번',
|
||||
field: 'MATERIAL_PART_NO',
|
||||
editor: 'input'
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '정미수량',
|
||||
field: 'NET_QTY',
|
||||
editor: false,
|
||||
formatter: function(cell) {
|
||||
// 소요량 × 제작수량 (소수점 무조건 올림)
|
||||
var data = cell.getRow().getData();
|
||||
var requiredQty = parseFloat(data.REQUIRED_QTY) || 0;
|
||||
var productionQty = parseFloat(data.PRODUCTION_QTY) || 0;
|
||||
var netQty = requiredQty * productionQty;
|
||||
return Math.ceil(netQty); // 무조건 올림
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '발주수량',
|
||||
field: 'PO_QTY',
|
||||
editor: 'number',
|
||||
editorParams: {
|
||||
min: 0,
|
||||
step: 1
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -409,7 +462,10 @@ function fn_initGrid() {
|
||||
width: 150,
|
||||
title: '공급업체',
|
||||
field: 'VENDOR',
|
||||
editor: 'input'
|
||||
editor: false, // 구매쪽에서 입력
|
||||
formatter: function(cell) {
|
||||
return cell.getValue() || '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
@@ -417,40 +473,141 @@ function fn_initGrid() {
|
||||
width: 100,
|
||||
title: '단가',
|
||||
field: 'UNIT_PRICE',
|
||||
editor: 'number',
|
||||
editorParams: {
|
||||
min: 0,
|
||||
step: 0.01
|
||||
},
|
||||
editor: false, // 구매쪽에서 입력
|
||||
formatter: function(cell) {
|
||||
var value = cell.getValue();
|
||||
return value ? Number(value).toLocaleString() : '0';
|
||||
return value ? Number(value).toLocaleString() : '-';
|
||||
}
|
||||
},
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 100,
|
||||
title: '총단가',
|
||||
title: '금액',
|
||||
field: 'TOTAL_PRICE',
|
||||
editor: false,
|
||||
formatter: function(cell) {
|
||||
// 발주수량 × 단가
|
||||
// 항목수량 × 단가
|
||||
var data = cell.getRow().getData();
|
||||
var poQty = parseFloat(data.PO_QTY) || 0;
|
||||
var itemQty = parseFloat(data.ITEM_QTY) || 0;
|
||||
var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
|
||||
var totalPrice = poQty * unitPrice;
|
||||
return totalPrice.toLocaleString();
|
||||
var totalPrice = itemQty * unitPrice;
|
||||
return totalPrice > 0 ? totalPrice.toLocaleString() : '-';
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 구매 컬럼 그룹 (제작수량 기준)
|
||||
// columns.push({
|
||||
// title: '구매',
|
||||
// headerHozAlign: 'center',
|
||||
// columns: [
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'left',
|
||||
// width: 150,
|
||||
// title: '소재품번',
|
||||
// field: 'MATERIAL_PART_NO',
|
||||
// editor: 'input'
|
||||
// },
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'center',
|
||||
// width: 100,
|
||||
// title: '정미수량',
|
||||
// field: 'NET_QTY',
|
||||
// editor: false,
|
||||
// formatter: function(cell) {
|
||||
// // 소요량 × 제작수량 (소수점 무조건 올림)
|
||||
// var data = cell.getRow().getData();
|
||||
// var requiredQty = parseFloat(data.REQUIRED_QTY) || 0;
|
||||
// var productionQty = parseFloat(data.PRODUCTION_QTY) || 0;
|
||||
// var netQty = requiredQty * productionQty;
|
||||
// return Math.ceil(netQty); // 무조건 올림
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'center',
|
||||
// width: 100,
|
||||
// title: '발주수량',
|
||||
// field: 'PO_QTY',
|
||||
// editor: 'number',
|
||||
// editorParams: {
|
||||
// min: 0,
|
||||
// step: 1
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'left',
|
||||
// width: 150,
|
||||
// title: '공급업체',
|
||||
// field: 'VENDOR',
|
||||
// editor: 'input'
|
||||
// },
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'right',
|
||||
// width: 100,
|
||||
// title: '단가',
|
||||
// field: 'UNIT_PRICE',
|
||||
// editor: 'number',
|
||||
// editorParams: {
|
||||
// min: 0,
|
||||
// step: 0.01
|
||||
// },
|
||||
// formatter: function(cell) {
|
||||
// var value = cell.getValue();
|
||||
// return value ? Number(value).toLocaleString() : '0';
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// headerHozAlign: 'center',
|
||||
// hozAlign: 'right',
|
||||
// width: 100,
|
||||
// title: '총단가',
|
||||
// field: 'TOTAL_PRICE',
|
||||
// editor: false,
|
||||
// formatter: function(cell) {
|
||||
// // 발주수량 × 단가
|
||||
// var data = cell.getRow().getData();
|
||||
// var poQty = parseFloat(data.PO_QTY) || 0;
|
||||
// var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
|
||||
// var totalPrice = poQty * unitPrice;
|
||||
// return totalPrice.toLocaleString();
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// });
|
||||
|
||||
// 서버에서 전달받은 데이터 확인
|
||||
var bomTreeData = ${bomTreeListJson};
|
||||
console.log("bomTreeData:", bomTreeData);
|
||||
console.log("bomTreeData length:", bomTreeData ? bomTreeData.length : 0);
|
||||
|
||||
// 데이터 전처리: SUPPLY_TYPE 기본값 설정
|
||||
if(bomTreeData && bomTreeData.length > 0) {
|
||||
bomTreeData.forEach(function(row) {
|
||||
// SUPPLY_TYPE이 없으면 '사급'으로 설정
|
||||
if(!row.SUPPLY_TYPE) {
|
||||
row.SUPPLY_TYPE = '사급';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 모든 컬럼에 headerSort: false 일괄 적용
|
||||
columns.forEach(function(col) {
|
||||
col.headerSort = false;
|
||||
// 중첩된 컬럼이 있는 경우 (수준 컬럼 등)
|
||||
if(col.columns) {
|
||||
col.columns.forEach(function(subCol) {
|
||||
subCol.headerSort = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Tabulator 생성
|
||||
_tabulGrid = new Tabulator("#mBomTableWrap", {
|
||||
layout: "fitData",
|
||||
@@ -559,22 +716,102 @@ function fn_searchMbom(searchParams) {
|
||||
});
|
||||
}
|
||||
|
||||
// 가공납기/연삭납기 일괄 적용
|
||||
function applyBulkDeadline(processingDeadline, grindingDeadline) {
|
||||
if(!_tabulGrid) {
|
||||
console.error("그리드가 초기화되지 않았습니다.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
console.log("일괄 적용 시작");
|
||||
console.log("가공납기:", processingDeadline);
|
||||
console.log("연삭납기:", grindingDeadline);
|
||||
|
||||
var allRows = _tabulGrid.getRows();
|
||||
console.log("전체 행 수:", allRows.length);
|
||||
|
||||
var updatedCount = 0;
|
||||
|
||||
allRows.forEach(function(row) {
|
||||
var updateData = {};
|
||||
|
||||
if(processingDeadline) {
|
||||
updateData.PROCESSING_DEADLINE = processingDeadline;
|
||||
}
|
||||
if(grindingDeadline) {
|
||||
updateData.GRINDING_DEADLINE = grindingDeadline;
|
||||
}
|
||||
|
||||
// 행 직접 업데이트
|
||||
row.update(updateData);
|
||||
updatedCount++;
|
||||
});
|
||||
|
||||
console.log("업데이트 완료 - 업데이트된 행:", updatedCount);
|
||||
return updatedCount;
|
||||
}
|
||||
|
||||
// M-BOM 트리 데이터 수집 (저장용)
|
||||
function getMbomTreeData() {
|
||||
var allData = _tabulGrid.getData();
|
||||
|
||||
// 데이터 구조 변환 (필요한 필드만 추출)
|
||||
// 숫자 변환 헬퍼 함수
|
||||
function toNumber(value) {
|
||||
if (value === null || value === undefined || value === '') return null;
|
||||
var num = parseFloat(value);
|
||||
return isNaN(num) ? null : num;
|
||||
}
|
||||
|
||||
// 데이터 구조 변환 (MBOM_DETAIL 테이블에 필요한 모든 필드 포함)
|
||||
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
|
||||
// BOM 구조 정보
|
||||
parentObjid: row.PARENT_OBJID,
|
||||
childObjid: row.CHILD_OBJID,
|
||||
seq: row.SEQ,
|
||||
level: row.LEVEL,
|
||||
|
||||
// 품목 정보
|
||||
partObjid: row.PART_OBJID || row.LAST_PART_OBJID,
|
||||
partNo: row.PART_NO,
|
||||
partName: row.PART_NAME,
|
||||
|
||||
// 수량 정보 (숫자로 변환)
|
||||
qty: toNumber(row.QTY_TEMP || row.QTY || row.ITEM_QTY),
|
||||
unit: row.UNIT,
|
||||
|
||||
// 생산 정보
|
||||
supplyType: row.SUPPLY_TYPE,
|
||||
makeOrBuy: row.MAKE_OR_BUY,
|
||||
rawMaterial: row.RAW_MATERIAL,
|
||||
rawMaterialSpec: row.RAW_MATERIAL_SPEC,
|
||||
rawMaterialSize: row.SIZE,
|
||||
rawMaterialPartNo: row.RAW_MATERIAL_NO,
|
||||
processingVendor: row.PROCESSING_VENDOR,
|
||||
processingDeadline: row.PROCESSING_DEADLINE,
|
||||
grindingDeadline: row.GRINDING_DEADLINE,
|
||||
requiredQty: toNumber(row.REQUIRED_QTY),
|
||||
orderQty: toNumber(row.ORDER_QTY),
|
||||
productionQty: toNumber(row.PRODUCTION_QTY),
|
||||
stockQty: toNumber(row.STOCK_QTY),
|
||||
shortageQty: toNumber(row.SHORTAGE_QTY),
|
||||
|
||||
// 구매 정보
|
||||
vendor: row.VENDOR,
|
||||
unitPrice: toNumber(row.UNIT_PRICE),
|
||||
totalPrice: toNumber(row.TOTAL_PRICE),
|
||||
currency: row.CURRENCY,
|
||||
leadTime: toNumber(row.LEAD_TIME),
|
||||
minOrderQty: toNumber(row.MIN_ORDER_QTY),
|
||||
|
||||
// 기타
|
||||
status: row.STATUS,
|
||||
remark: row.REMARK,
|
||||
revision: row.REVISION,
|
||||
spec: row.SPEC,
|
||||
writer: row.WRITER,
|
||||
editer: row.EDITER,
|
||||
objid: row.OBJID
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -898,12 +898,26 @@ public class PartMngController {
|
||||
public String structureBomCopyFormPopup(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
try {
|
||||
String objId = CommonUtils.checkNull((String)paramMap.get("objId"));
|
||||
String partNo = CommonUtils.checkNull((String)paramMap.get("partNo"));
|
||||
String partName = CommonUtils.checkNull((String)paramMap.get("partName"));
|
||||
|
||||
System.out.println("========== structureBomCopyFormPopup ==========");
|
||||
System.out.println("objId: " + objId);
|
||||
System.out.println("partNo: " + partNo);
|
||||
System.out.println("partName: " + partName);
|
||||
|
||||
// objId를 항상 설정 (음수 포함)
|
||||
request.setAttribute("TARGET_OBJID", objId);
|
||||
|
||||
// URL 파라미터로 전달된 품번/품명이 있으면 우선 사용
|
||||
if(!"".equals(partNo) && !"".equals(partName)) {
|
||||
Map<String, Object> urlParamInfo = new HashMap<>();
|
||||
urlParamInfo.put("PART_NO", partNo);
|
||||
urlParamInfo.put("PART_NAME", partName);
|
||||
request.setAttribute("urlParamInfo", urlParamInfo);
|
||||
System.out.println("URL 파라미터 사용: " + partNo + " / " + partName);
|
||||
}
|
||||
|
||||
// objId가 유효한 경우 프로젝트 정보 조회 (음수도 유효한 OBJID임)
|
||||
if(!"".equals(objId) && !"-1".equals(objId)) {
|
||||
try {
|
||||
@@ -929,6 +943,13 @@ public class PartMngController {
|
||||
Map code_map = new HashMap();
|
||||
code_map.put("rev", commonService.bizMakeOptionList("", "", "common.getRevNoselect"));
|
||||
code_map.put("product_code", commonService.bizMakeOptionList("", "", "common.getProductNoselect"));
|
||||
|
||||
// E-BOM 목록 (기존 getActiveBomList 사용)
|
||||
code_map.put("ebom_list", commonService.bizMakeOptionList("", "", "partMng.getActiveBomList"));
|
||||
|
||||
// M-BOM 목록 (bizMakeOptionList 사용)
|
||||
code_map.put("mbom_list", commonService.bizMakeOptionList("", "", "productionplanning.getMbomListForSelect2"));
|
||||
|
||||
request.setAttribute("code_map", code_map);
|
||||
|
||||
} catch(Exception e) {
|
||||
|
||||
@@ -1084,17 +1084,63 @@ public class ProductionPlanningController extends BaseService {
|
||||
|
||||
if(projectInfo != null) {
|
||||
System.out.println("BOM_REPORT_OBJID: " + projectInfo.get("BOM_REPORT_OBJID"));
|
||||
System.out.println("SOURCE_BOM_TYPE: " + projectInfo.get("SOURCE_BOM_TYPE"));
|
||||
System.out.println("SOURCE_EBOM_OBJID: " + projectInfo.get("SOURCE_EBOM_OBJID"));
|
||||
System.out.println("SOURCE_MBOM_OBJID: " + projectInfo.get("SOURCE_MBOM_OBJID"));
|
||||
System.out.println("MBOM_STATUS: " + projectInfo.get("MBOM_STATUS"));
|
||||
|
||||
// 1. 먼저 저장된 M-BOM 데이터가 있는지 확인
|
||||
String bomReportObjid = CommonUtils.checkNull(projectInfo.get("BOM_REPORT_OBJID"));
|
||||
if(!"".equals(bomReportObjid)) {
|
||||
Map<String, Object> bomParam = new HashMap<>();
|
||||
bomParam.put("bomReportObjId", bomReportObjid);
|
||||
bomParam.put("search_type", "working");
|
||||
String bomReportObjid = "";
|
||||
boolean isSavedMbom = false;
|
||||
|
||||
// 1. 먼저 저장된 M-BOM이 있는지 확인 (MBOM_HEADER 테이블)
|
||||
Map<String, Object> savedMbomParam = new HashMap<>();
|
||||
savedMbomParam.put("projectObjId", objId);
|
||||
Map<String, Object> savedMbom = commonService.selectOne("productionplanning.getLatestMbomByProjectId", request, savedMbomParam);
|
||||
|
||||
if(savedMbom != null && savedMbom.get("OBJID") != null) {
|
||||
// 저장된 M-BOM이 있으면 해당 M-BOM 사용
|
||||
bomReportObjid = CommonUtils.checkNull(savedMbom.get("OBJID"));
|
||||
isSavedMbom = true;
|
||||
System.out.println("저장된 M-BOM 사용: " + bomReportObjid);
|
||||
} else {
|
||||
// 저장된 M-BOM이 없으면 할당된 BOM 정보 사용
|
||||
String sourceBomType = CommonUtils.checkNull(projectInfo.get("SOURCE_BOM_TYPE"));
|
||||
String sourceEbomObjId = CommonUtils.checkNull(projectInfo.get("SOURCE_EBOM_OBJID"));
|
||||
String sourceMbomObjId = CommonUtils.checkNull(projectInfo.get("SOURCE_MBOM_OBJID"));
|
||||
|
||||
List<Map<String, Object>> mbomDetailList = commonService.selectList("partMng.getBOMTreeList", request, bomParam);
|
||||
System.out.println("mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
|
||||
if("EBOM".equals(sourceBomType) && !"".equals(sourceEbomObjId)) {
|
||||
bomReportObjid = sourceEbomObjId;
|
||||
System.out.println("할당된 E-BOM 사용: " + bomReportObjid);
|
||||
} else if("MBOM".equals(sourceBomType) && !"".equals(sourceMbomObjId)) {
|
||||
bomReportObjid = sourceMbomObjId;
|
||||
System.out.println("할당된 M-BOM 사용: " + bomReportObjid);
|
||||
} else {
|
||||
bomReportObjid = CommonUtils.checkNull(projectInfo.get("BOM_REPORT_OBJID"));
|
||||
System.out.println("기존 BOM_REPORT_OBJID 사용: " + bomReportObjid);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. BOM 데이터 조회
|
||||
List<Map<String, Object>> mbomDetailList = null;
|
||||
|
||||
if(!"".equals(bomReportObjid)) {
|
||||
if(isSavedMbom) {
|
||||
// 저장된 M-BOM: MBOM_DETAIL 테이블에서 조회
|
||||
Map<String, Object> mbomParam = new HashMap<>();
|
||||
mbomParam.put("mbomHeaderObjid", bomReportObjid);
|
||||
List tempList = commonService.selectList("productionplanning.getSavedMbomTreeList", request, mbomParam);
|
||||
// MyBatis resultType="map"은 소문자로 반환하므로 대문자로 변환 필요
|
||||
mbomDetailList = (List<Map<String, Object>>) (List<?>) CommonUtils.keyChangeUpperList(tempList);
|
||||
System.out.println("저장된 M-BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
|
||||
} else {
|
||||
// 할당된 E-BOM/M-BOM: BOM_PART_QTY 테이블에서 조회
|
||||
Map<String, Object> bomParam = new HashMap<>();
|
||||
bomParam.put("bomReportObjId", bomReportObjid);
|
||||
bomParam.put("search_type", "working");
|
||||
mbomDetailList = commonService.selectList("partMng.getBOMTreeList", request, bomParam);
|
||||
System.out.println("할당된 BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
|
||||
}
|
||||
}
|
||||
|
||||
if(mbomDetailList != null && !mbomDetailList.isEmpty()) {
|
||||
// 저장된 M-BOM 데이터가 있으면 이를 표시
|
||||
@@ -1121,8 +1167,6 @@ public class ProductionPlanningController extends BaseService {
|
||||
request.setAttribute("bomTreeListJson", gson.toJson(mbomDetailList));
|
||||
return "/productionplanning/mBomPopupLeft";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
@@ -1196,49 +1240,6 @@ public class ProductionPlanningController extends BaseService {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* M-BOM 저장
|
||||
* @param request
|
||||
* @param paramMap
|
||||
* @return
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/saveMbom.do")
|
||||
public Map<String, Object> saveMbom(HttpServletRequest request, @RequestBody Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
// M-BOM 데이터 저장 로직
|
||||
List<Map<String, Object>> mbomData = (List<Map<String, Object>>) paramMap.get("mbomData");
|
||||
String partNo = CommonUtils.checkNull(paramMap.get("partNo"));
|
||||
String partName = CommonUtils.checkNull(paramMap.get("partName"));
|
||||
String mbomPartNo = CommonUtils.checkNull(paramMap.get("mbomPartNo")); // 기존 M-BOM 품번
|
||||
boolean isUpdate = paramMap.get("isUpdate") != null && (Boolean)paramMap.get("isUpdate"); // 수정 여부
|
||||
|
||||
if(mbomData == null || mbomData.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "저장할 데이터가 없습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// M-BOM 저장 서비스 호출
|
||||
// isUpdate가 true이면 기존 M-BOM 품번 유지, false이면 새로 생성
|
||||
int saveResult = productionPlanningService.saveMbom(request, mbomData, partNo, partName, mbomPartNo, isUpdate);
|
||||
|
||||
if(saveResult > 0) {
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("message", isUpdate ? "M-BOM이 수정되었습니다." : "M-BOM이 저장되었습니다.");
|
||||
} else {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "저장에 실패했습니다.");
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 품번으로 최신 M-BOM 조회 (Machine 이외 제품용)
|
||||
* @param request
|
||||
@@ -1279,16 +1280,21 @@ public class ProductionPlanningController extends BaseService {
|
||||
public Map<String, Object> getMbomHistory(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
String mbomPartNo = CommonUtils.checkNull(paramMap.get("mbomPartNo"));
|
||||
String projectObjId = CommonUtils.checkNull(paramMap.get("projectObjId"));
|
||||
|
||||
if("".equals(mbomPartNo)) {
|
||||
if("".equals(projectObjId)) {
|
||||
resultMap.put("historyList", new ArrayList<>());
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// M-BOM 이력 조회 (PART_BOM_REPORT 테이블에서 동일 PART_NO로 조회)
|
||||
System.out.println("========== getMbomHistory ==========");
|
||||
System.out.println("projectObjId: " + projectObjId);
|
||||
|
||||
// M-BOM 이력 조회 (PROJECT_OBJID로 조회)
|
||||
List<Map<String, Object>> historyList = commonService.selectList("productionplanning.getMbomHistory", request, paramMap);
|
||||
|
||||
System.out.println("조회된 이력 수: " + (historyList != null ? historyList.size() : 0));
|
||||
|
||||
resultMap.put("historyList", historyList != null ? historyList : new ArrayList<>());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -1357,4 +1363,170 @@ public class ProductionPlanningController extends BaseService {
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* BOM 할당 정보 저장 (M-BOM 기준 설정)
|
||||
* PROJECT_MGMT 테이블에 E-BOM 또는 M-BOM 할당 정보만 저장
|
||||
* 실제 M-BOM 생성은 M-BOM 상세 팝업에서 수행
|
||||
*/
|
||||
@RequestMapping("/productionplanning/saveBomAssignment.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> saveBomAssignment(HttpServletRequest request, @RequestBody Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
System.out.println("========== saveBomAssignment ==========");
|
||||
System.out.println("projectObjId: " + paramMap.get("projectObjId"));
|
||||
System.out.println("sourceBomType: " + paramMap.get("sourceBomType"));
|
||||
System.out.println("sourceBomObjId: " + paramMap.get("sourceBomObjId"));
|
||||
System.out.println("partNo: " + paramMap.get("partNo"));
|
||||
System.out.println("partName: " + paramMap.get("partName"));
|
||||
|
||||
// PROJECT_MGMT 테이블 업데이트
|
||||
boolean updateResult = productionPlanningService.saveBomAssignment(request, paramMap);
|
||||
|
||||
if(updateResult) {
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("message", "M-BOM 기준 정보가 저장되었습니다.");
|
||||
} else {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "저장에 실패했습니다.");
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
@RequestMapping("/productionplanning/getMbomAssignmentInfo.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> getMbomAssignmentInfo(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
String projectObjId = CommonUtils.checkNull((String)paramMap.get("projectObjId"));
|
||||
System.out.println("========== getMbomAssignmentInfo ==========");
|
||||
System.out.println("projectObjId: " + projectObjId);
|
||||
|
||||
// PROJECT_MGMT에서 BOM 할당 정보 조회
|
||||
Map<String, Object> assignmentInfo = productionPlanningService.getMbomAssignment(projectObjId);
|
||||
|
||||
if(assignmentInfo != null && !assignmentInfo.isEmpty()) {
|
||||
resultMap.putAll(assignmentInfo);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("error", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 프로젝트별 최신 M-BOM 조회
|
||||
*/
|
||||
@RequestMapping("/productionplanning/getLatestMbomByProjectId.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> getLatestMbomByProjectId(HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
String projectObjId = CommonUtils.checkNull((String)paramMap.get("projectObjId"));
|
||||
paramMap.put("projectObjId", projectObjId);
|
||||
|
||||
// MBOM_HEADER에서 해당 프로젝트의 최신 M-BOM 조회
|
||||
Map<String, Object> savedMbom = commonService.selectOne("productionplanning.getLatestMbomByProjectId", request, paramMap);
|
||||
|
||||
if(savedMbom != null && !savedMbom.isEmpty()) {
|
||||
resultMap = (Map<String, Object>) CommonUtils.toUpperCaseMapKey(savedMbom);
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("error", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* M-BOM 저장
|
||||
* 최초 저장 시: M-BOM 품번 생성 + 데이터 저장
|
||||
* 수정 저장 시: 동일 품번으로 업데이트 + 이력 저장
|
||||
*/
|
||||
@RequestMapping("/productionplanning/saveMbom.do")
|
||||
@ResponseBody
|
||||
public Map<String, Object> saveMbom(HttpServletRequest request, @RequestBody Map<String, Object> paramMap) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
try {
|
||||
System.out.println("========== saveMbom ==========");
|
||||
|
||||
String projectObjId = CommonUtils.checkNull((String)paramMap.get("projectObjId"));
|
||||
String mbomPartNo = CommonUtils.checkNull((String)paramMap.get("mbomPartNo"));
|
||||
boolean isUpdate = "true".equals(String.valueOf(paramMap.get("isUpdate")));
|
||||
|
||||
System.out.println("projectObjId: " + projectObjId);
|
||||
System.out.println("mbomPartNo: " + mbomPartNo);
|
||||
System.out.println("isUpdate: " + isUpdate);
|
||||
|
||||
// PROJECT_MGMT에서 할당 정보 조회
|
||||
Map<String, Object> assignment = (Map<String, Object>) commonService.selectOne("productionplanning.getMbomAssignment", request, paramMap);
|
||||
|
||||
if(assignment == null || assignment.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "M-BOM 기준 정보가 없습니다. BOM 복사 팝업에서 먼저 기준을 설정해주세요.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
String sourceBomType = CommonUtils.checkNull(assignment.get("SOURCE_BOM_TYPE"));
|
||||
String sourceBomObjId = "";
|
||||
String baseBomPartNo = "";
|
||||
|
||||
if("EBOM".equals(sourceBomType)) {
|
||||
sourceBomObjId = CommonUtils.checkNull(assignment.get("SOURCE_EBOM_OBJID"));
|
||||
baseBomPartNo = CommonUtils.checkNull(assignment.get("EBOM_PART_NO"));
|
||||
} else if("MBOM".equals(sourceBomType)) {
|
||||
sourceBomObjId = CommonUtils.checkNull(assignment.get("SOURCE_MBOM_OBJID"));
|
||||
baseBomPartNo = CommonUtils.checkNull(assignment.get("MBOM_NO"));
|
||||
}
|
||||
|
||||
if(sourceBomObjId.isEmpty()) {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "기준 BOM 정보가 올바르지 않습니다.");
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// M-BOM 품번 생성 또는 사용
|
||||
String finalMbomNo = "";
|
||||
|
||||
if(!isUpdate || mbomPartNo.isEmpty()) {
|
||||
// 최초 저장: M-BOM 품번 생성
|
||||
finalMbomNo = productionPlanningService.generateMbomNo(request, sourceBomType, baseBomPartNo);
|
||||
System.out.println("생성된 M-BOM 품번: " + finalMbomNo);
|
||||
} else {
|
||||
// 수정 저장: 기존 품번 사용
|
||||
finalMbomNo = mbomPartNo;
|
||||
System.out.println("기존 M-BOM 품번 사용: " + finalMbomNo);
|
||||
}
|
||||
|
||||
paramMap.put("mbomNo", finalMbomNo);
|
||||
paramMap.put("sourceBomType", sourceBomType);
|
||||
paramMap.put("sourceBomObjId", sourceBomObjId);
|
||||
paramMap.put("isUpdate", isUpdate);
|
||||
|
||||
// M-BOM 저장
|
||||
boolean saveResult = productionPlanningService.saveMbom(request, paramMap);
|
||||
|
||||
if(saveResult) {
|
||||
resultMap.put("result", "success");
|
||||
resultMap.put("message", isUpdate ? "M-BOM이 수정되었습니다." : "M-BOM이 생성되었습니다.");
|
||||
resultMap.put("mbomNo", finalMbomNo);
|
||||
} else {
|
||||
resultMap.put("result", "fail");
|
||||
resultMap.put("message", "M-BOM 저장에 실패했습니다.");
|
||||
}
|
||||
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
resultMap.put("result", "error");
|
||||
resultMap.put("message", "오류가 발생했습니다: " + e.getMessage());
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8099,4 +8099,18 @@ SELECT PM.OBJID
|
||||
)
|
||||
</update>
|
||||
|
||||
<!-- E-BOM 목록 조회 (셀렉트박스용) -->
|
||||
<select id="getEbomList" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
OBJID,
|
||||
PART_NO,
|
||||
PART_NAME,
|
||||
REV,
|
||||
SPEC_NAME,
|
||||
REGDATE
|
||||
FROM PART_BOM_REPORT
|
||||
WHERE STATUS = 'Y'
|
||||
ORDER BY REGDATE DESC, PART_NO
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -2944,29 +2944,47 @@
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS EBOM_REGDATE,
|
||||
COALESCE(PM.MBOM_STATUS, '') AS MBOM_STATUS,
|
||||
-- M-BOM 상태: 새 MBOM_HEADER 테이블에서 조회
|
||||
COALESCE(
|
||||
(SELECT PBR.PART_NO
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.BOM_REPORT_OBJID
|
||||
AND PM.MBOM_STATUS = 'Y'
|
||||
LIMIT 1),
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) > 0 THEN 'Y'
|
||||
ELSE COALESCE(PM.MBOM_STATUS, '')
|
||||
END
|
||||
FROM MBOM_HEADER MH
|
||||
WHERE MH.PROJECT_OBJID = PM.OBJID::VARCHAR
|
||||
AND MH.STATUS = 'Y'
|
||||
LIMIT 1),
|
||||
COALESCE(PM.MBOM_STATUS, '')
|
||||
) AS MBOM_STATUS,
|
||||
-- M-BOM 품번: 새 MBOM_HEADER 테이블에서 조회
|
||||
COALESCE(
|
||||
(SELECT MH.MBOM_NO
|
||||
FROM MBOM_HEADER MH
|
||||
WHERE MH.PROJECT_OBJID = PM.OBJID::VARCHAR
|
||||
AND MH.STATUS = 'Y'
|
||||
ORDER BY MH.REGDATE DESC
|
||||
LIMIT 1),
|
||||
''
|
||||
) AS MBOM_PART_NO,
|
||||
'1.0' AS MBOM_VERSION,
|
||||
-- M-BOM 저장일: 새 MBOM_HEADER 테이블에서 조회
|
||||
COALESCE(
|
||||
(SELECT TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD')
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.BOM_REPORT_OBJID
|
||||
AND PM.MBOM_STATUS = 'Y'
|
||||
LIMIT 1),
|
||||
(SELECT TO_CHAR(MH.REGDATE, 'YYYY-MM-DD')
|
||||
FROM MBOM_HEADER MH
|
||||
WHERE MH.PROJECT_OBJID = PM.OBJID::VARCHAR
|
||||
AND MH.STATUS = 'Y'
|
||||
ORDER BY MH.REGDATE DESC
|
||||
LIMIT 1),
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD')
|
||||
) AS MBOM_REGDATE
|
||||
FROM
|
||||
PROJECT_MGMT PM
|
||||
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
|
||||
AND PM.PART_OBJID = CI.PART_OBJID
|
||||
-- CONTRACT_ITEM과 LEFT JOIN하여 품목별로 펼쳐서 보이기
|
||||
LEFT JOIN CONTRACT_ITEM CI ON CM.OBJID::VARCHAR = CI.CONTRACT_OBJID
|
||||
-- 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
|
||||
@@ -3024,6 +3042,10 @@
|
||||
PM.BOM_REPORT_OBJID,
|
||||
PM.PART_NO,
|
||||
PM.PART_NAME,
|
||||
PM.SOURCE_BOM_TYPE,
|
||||
PM.SOURCE_EBOM_OBJID,
|
||||
PM.SOURCE_MBOM_OBJID,
|
||||
PM.QUANTITY,
|
||||
COALESCE(
|
||||
(SELECT PBR.PART_NO
|
||||
FROM PART_BOM_REPORT PBR
|
||||
@@ -3268,21 +3290,6 @@
|
||||
BPQ.SEQ
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 품번 최대 순번 조회 (같은 날짜) -->
|
||||
<select id="getMaxMbomSeqByDate" parameterType="map" resultType="int">
|
||||
SELECT
|
||||
COALESCE(MAX(
|
||||
CAST(
|
||||
SUBSTRING(PART_NO FROM '.+-([0-9]+)$') AS INTEGER
|
||||
)
|
||||
), 0) AS MAX_SEQ
|
||||
FROM
|
||||
PART_BOM_REPORT
|
||||
WHERE
|
||||
PART_NO LIKE #{prefix} || '%'
|
||||
AND STATUS = 'Y'
|
||||
</select>
|
||||
|
||||
<!-- 품번으로 최신 M-BOM 조회 (Machine 이외 제품용) -->
|
||||
<select id="getLatestMbomByPartNo" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
@@ -3314,85 +3321,26 @@
|
||||
<!-- M-BOM 이력 조회 -->
|
||||
<select id="getMbomHistory" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
PBR.OBJID,
|
||||
PBR.PART_NO AS MBOM_PART_NO,
|
||||
TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS REGDATE,
|
||||
PBR.REGUSER,
|
||||
COALESCE(PBR.REMARK, '') AS REMARK,
|
||||
PBR.STATUS
|
||||
MH.OBJID,
|
||||
MH.MBOM_HEADER_OBJID,
|
||||
MH.CHANGE_TYPE,
|
||||
MH.CHANGE_DESCRIPTION,
|
||||
MH.BEFORE_DATA,
|
||||
MH.AFTER_DATA,
|
||||
MH.CHANGE_USER,
|
||||
TO_CHAR(MH.CHANGE_DATE, 'YYYY-MM-DD HH24:MI:SS') AS CHANGE_DATE,
|
||||
-- MBOM_HEADER 정보 조인
|
||||
MHD.MBOM_NO AS MBOM_PART_NO,
|
||||
MHD.PROJECT_OBJID,
|
||||
TO_CHAR(MHD.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS REGDATE
|
||||
FROM
|
||||
PART_BOM_REPORT PBR
|
||||
MBOM_HISTORY MH
|
||||
INNER JOIN MBOM_HEADER MHD ON MH.MBOM_HEADER_OBJID = MHD.OBJID
|
||||
WHERE
|
||||
PBR.PART_NO = #{mbomPartNo}
|
||||
ORDER BY PBR.REGDATE DESC
|
||||
MHD.PROJECT_OBJID = #{projectObjId}
|
||||
ORDER BY MH.CHANGE_DATE DESC
|
||||
</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으로 복사 */
|
||||
@@ -3463,4 +3411,312 @@
|
||||
PART_NAME = #{PART_NAME}
|
||||
WHERE OBJID::VARCHAR = #{PROJECT_OBJID}
|
||||
</insert>
|
||||
|
||||
<!-- M-BOM 관련 쿼리 -->
|
||||
|
||||
<!-- BOM 할당 정보 저장 (M-BOM 기준 설정) -->
|
||||
<update id="saveBomAssignment" parameterType="map">
|
||||
UPDATE PROJECT_MGMT
|
||||
SET
|
||||
SOURCE_BOM_TYPE = #{sourceBomType},
|
||||
SOURCE_EBOM_OBJID = CASE WHEN #{sourceBomType} = 'EBOM' THEN #{sourceBomObjId} ELSE NULL END,
|
||||
SOURCE_MBOM_OBJID = CASE WHEN #{sourceBomType} = 'MBOM' THEN #{sourceBomObjId} ELSE NULL END
|
||||
WHERE OBJID::VARCHAR = #{projectObjId}
|
||||
</update>
|
||||
|
||||
<!-- 프로젝트의 최신 저장된 M-BOM 조회 -->
|
||||
<select id="getLatestMbomByProjectId" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
OBJID,
|
||||
MBOM_NO,
|
||||
SOURCE_BOM_TYPE,
|
||||
SOURCE_EBOM_OBJID,
|
||||
SOURCE_MBOM_OBJID,
|
||||
PROJECT_OBJID,
|
||||
STATUS,
|
||||
REGDATE
|
||||
FROM MBOM_HEADER
|
||||
WHERE PROJECT_OBJID = #{projectObjId}
|
||||
AND STATUS = 'Y'
|
||||
ORDER BY REGDATE DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 할당 정보 조회 -->
|
||||
<select id="getMbomAssignment" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
PM.OBJID,
|
||||
PM.SOURCE_BOM_TYPE,
|
||||
PM.SOURCE_EBOM_OBJID,
|
||||
PM.SOURCE_MBOM_OBJID,
|
||||
PM.PART_NO,
|
||||
PM.PART_NAME,
|
||||
PM.PRODUCT,
|
||||
CODE_NAME(PM.PRODUCT) AS PRODUCT_NAME,
|
||||
-- E-BOM 정보
|
||||
CASE WHEN PM.SOURCE_BOM_TYPE = 'EBOM' THEN
|
||||
(SELECT PART_NO FROM PART_BOM_REPORT WHERE OBJID = PM.SOURCE_EBOM_OBJID)
|
||||
END AS EBOM_PART_NO,
|
||||
CASE WHEN PM.SOURCE_BOM_TYPE = 'EBOM' THEN
|
||||
(SELECT PART_NAME FROM PART_BOM_REPORT WHERE OBJID = PM.SOURCE_EBOM_OBJID)
|
||||
END AS EBOM_PART_NAME,
|
||||
-- M-BOM 정보
|
||||
CASE WHEN PM.SOURCE_BOM_TYPE = 'MBOM' THEN
|
||||
(SELECT MBOM_NO FROM MBOM_HEADER WHERE OBJID = PM.SOURCE_MBOM_OBJID)
|
||||
END AS MBOM_NO,
|
||||
CASE WHEN PM.SOURCE_BOM_TYPE = 'MBOM' THEN
|
||||
(SELECT PART_NAME FROM MBOM_HEADER WHERE OBJID = PM.SOURCE_MBOM_OBJID)
|
||||
END AS MBOM_PART_NAME
|
||||
FROM PROJECT_MGMT PM
|
||||
WHERE PM.OBJID::VARCHAR = #{projectObjId}
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 품번 생성 - 같은 날짜의 최대 순번 조회 -->
|
||||
<select id="getMaxMbomSeqByDate" parameterType="map" resultType="int">
|
||||
SELECT COALESCE(MAX(
|
||||
CAST(
|
||||
SUBSTRING(
|
||||
MBOM_NO FROM LENGTH(MBOM_NO) - 1 FOR 2
|
||||
) AS INTEGER
|
||||
)
|
||||
), 0) AS MAX_SEQ
|
||||
FROM MBOM_HEADER
|
||||
WHERE MBOM_NO LIKE #{mbomPrefix} || '%'
|
||||
AND REGDATE::DATE = CURRENT_DATE
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 품번으로 조회 -->
|
||||
<select id="getMbomByNo" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT * FROM MBOM_HEADER
|
||||
WHERE PROJECT_OBJID = #{projectObjId}
|
||||
ORDER BY REGDATE DESC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
<!-- MBOM_HEADER 삽입 -->
|
||||
<insert id="insertMbomHeader" parameterType="map">
|
||||
INSERT INTO MBOM_HEADER (
|
||||
OBJID, MBOM_NO, SOURCE_BOM_TYPE, SOURCE_EBOM_OBJID, SOURCE_MBOM_OBJID,
|
||||
PROJECT_OBJID, PART_NO, PART_NAME, STATUS, MBOM_STATUS,
|
||||
WRITER, REGDATE
|
||||
) VALUES (
|
||||
#{objid}, #{mbomNo}, #{sourceBomType}, #{sourceEbomObjid}, #{sourceMbomObjid},
|
||||
#{projectObjId}, #{partNo}, #{partName}, 'Y', 'DRAFT',
|
||||
#{sessionUserId}, NOW()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- MBOM_DETAIL 삽입 -->
|
||||
<insert id="insertMbomDetail" parameterType="map">
|
||||
INSERT INTO MBOM_DETAIL (
|
||||
OBJID, MBOM_HEADER_OBJID, PARENT_OBJID, CHILD_OBJID, SEQ, LEVEL,
|
||||
PART_OBJID, PART_NO, PART_NAME, QTY, UNIT,
|
||||
SUPPLY_TYPE, MAKE_OR_BUY,
|
||||
RAW_MATERIAL_PART_NO, RAW_MATERIAL_SPEC, RAW_MATERIAL, RAW_MATERIAL_SIZE,
|
||||
PROCESSING_VENDOR, PROCESSING_DEADLINE, GRINDING_DEADLINE,
|
||||
REQUIRED_QTY, ORDER_QTY, PRODUCTION_QTY, STOCK_QTY, SHORTAGE_QTY,
|
||||
VENDOR, UNIT_PRICE, TOTAL_PRICE, CURRENCY, LEAD_TIME, MIN_ORDER_QTY,
|
||||
STATUS, WRITER, REGDATE, REMARK
|
||||
) VALUES (
|
||||
#{objid}, #{mbomHeaderObjid}, #{parentObjid}, #{childObjid}, #{seq}, #{level},
|
||||
#{partObjid}, #{partNo}, #{partName}, #{qty}, #{unit},
|
||||
#{supplyType}, #{makeOrBuy},
|
||||
#{rawMaterialPartNo}, #{rawMaterialSpec}, #{rawMaterial}, #{rawMaterialSize},
|
||||
#{processingVendor}, #{processingDeadline}, #{grindingDeadline},
|
||||
#{requiredQty}, #{orderQty}, #{productionQty}, #{stockQty}, #{shortageQty},
|
||||
#{vendor}, #{unitPrice}, #{totalPrice}, #{currency}, #{leadTime}, #{minOrderQty},
|
||||
'ACTIVE', #{sessionUserId}, NOW(), #{remark}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- MBOM_HEADER 업데이트 -->
|
||||
<update id="updateMbomHeader" parameterType="map">
|
||||
UPDATE MBOM_HEADER
|
||||
SET
|
||||
PART_NO = #{partNo},
|
||||
PART_NAME = #{partName},
|
||||
EDITER = #{sessionUserId},
|
||||
EDIT_DATE = NOW()
|
||||
WHERE OBJID = #{mbomHeaderObjid}
|
||||
</update>
|
||||
|
||||
<!-- MBOM_DETAIL 삭제 (수정 시 기존 데이터 삭제 후 재삽입) -->
|
||||
<delete id="deleteMbomDetail" parameterType="map">
|
||||
DELETE FROM MBOM_DETAIL
|
||||
WHERE MBOM_HEADER_OBJID = #{mbomHeaderObjid}
|
||||
</delete>
|
||||
|
||||
<!-- MBOM_HISTORY 삽입 -->
|
||||
<insert id="insertMbomHistory" parameterType="map">
|
||||
INSERT INTO MBOM_HISTORY (
|
||||
OBJID, MBOM_HEADER_OBJID, CHANGE_TYPE, CHANGE_DESCRIPTION,
|
||||
BEFORE_DATA, AFTER_DATA, CHANGE_USER, CHANGE_DATE
|
||||
) VALUES (
|
||||
#{objid}, #{mbomHeaderObjid}, #{changeType}, #{changeDescription},
|
||||
#{beforeData}::JSONB, #{afterData}::JSONB, #{sessionUserId}, NOW()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- PROJECT_MGMT MBOM_STATUS 업데이트 -->
|
||||
<update id="updateProjectMbomStatus" parameterType="map">
|
||||
UPDATE PROJECT_MGMT
|
||||
SET
|
||||
MBOM_STATUS = 'Y',
|
||||
MBOM_WRITER = #{sessionUserId},
|
||||
MBOM_REGDATE = NOW()
|
||||
WHERE OBJID::VARCHAR = #{projectObjId}
|
||||
</update>
|
||||
|
||||
<!-- M-BOM 목록 조회 (셀렉트박스용) -->
|
||||
<select id="getMbomListForSelect" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
OBJID,
|
||||
MBOM_NO,
|
||||
SOURCE_BOM_TYPE,
|
||||
REGDATE,
|
||||
STATUS
|
||||
FROM MBOM_HEADER
|
||||
WHERE STATUS = 'Y'
|
||||
ORDER BY REGDATE DESC, MBOM_NO
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 목록 (bizMakeOptionList용) -->
|
||||
<select id="getMbomListForSelect2" parameterType="map" resultType="map">
|
||||
SELECT
|
||||
OBJID AS CODE,
|
||||
COALESCE(MBOM_NO, '') ||
|
||||
CASE WHEN SOURCE_BOM_TYPE IS NOT NULL AND SOURCE_BOM_TYPE != '' THEN ' (' || SOURCE_BOM_TYPE || ')' ELSE '' END AS NAME
|
||||
FROM MBOM_HEADER
|
||||
WHERE STATUS = 'Y'
|
||||
ORDER BY REGDATE DESC, MBOM_NO
|
||||
</select>
|
||||
|
||||
<!-- M-BOM 상세 리스트 조회 (이력용) -->
|
||||
<select id="getMbomDetailList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
OBJID,
|
||||
MBOM_HEADER_OBJID,
|
||||
PARENT_OBJID,
|
||||
CHILD_OBJID,
|
||||
SEQ,
|
||||
LEVEL,
|
||||
PART_OBJID,
|
||||
PART_NO,
|
||||
PART_NAME,
|
||||
QTY,
|
||||
UNIT,
|
||||
SUPPLY_TYPE,
|
||||
MAKE_OR_BUY,
|
||||
RAW_MATERIAL_PART_NO,
|
||||
RAW_MATERIAL_SPEC,
|
||||
RAW_MATERIAL,
|
||||
RAW_MATERIAL_SIZE,
|
||||
PROCESSING_VENDOR,
|
||||
PROCESSING_DEADLINE,
|
||||
GRINDING_DEADLINE,
|
||||
REQUIRED_QTY,
|
||||
ORDER_QTY,
|
||||
PRODUCTION_QTY,
|
||||
STOCK_QTY,
|
||||
SHORTAGE_QTY,
|
||||
VENDOR,
|
||||
UNIT_PRICE,
|
||||
TOTAL_PRICE,
|
||||
CURRENCY,
|
||||
LEAD_TIME,
|
||||
MIN_ORDER_QTY,
|
||||
STATUS,
|
||||
REMARK
|
||||
FROM
|
||||
MBOM_DETAIL
|
||||
WHERE
|
||||
MBOM_HEADER_OBJID = #{mbomHeaderObjid}
|
||||
AND STATUS = 'ACTIVE'
|
||||
ORDER BY SEQ
|
||||
</select>
|
||||
|
||||
<!-- 저장된 M-BOM 트리 조회 (MBOM_DETAIL 테이블) -->
|
||||
<select id="getSavedMbomTreeList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
MD.OBJID,
|
||||
MD.MBOM_HEADER_OBJID AS BOM_REPORT_OBJID,
|
||||
MD.PARENT_OBJID,
|
||||
MD.CHILD_OBJID,
|
||||
MD.SEQ,
|
||||
MD.LEVEL,
|
||||
MD.PART_OBJID,
|
||||
MD.PART_NO,
|
||||
MD.PART_NAME,
|
||||
MD.QTY,
|
||||
MD.QTY AS QTY_TEMP,
|
||||
MD.QTY AS ITEM_QTY,
|
||||
MD.UNIT,
|
||||
-- 생산 정보
|
||||
MD.SUPPLY_TYPE,
|
||||
MD.MAKE_OR_BUY,
|
||||
MD.RAW_MATERIAL_PART_NO AS RAW_MATERIAL_NO, -- JSP 그리드 필드명에 맞춤
|
||||
MD.RAW_MATERIAL_SPEC,
|
||||
MD.RAW_MATERIAL,
|
||||
MD.RAW_MATERIAL_SIZE AS SIZE, -- JSP 그리드 필드명에 맞춤
|
||||
MD.PROCESSING_VENDOR,
|
||||
MD.PROCESSING_DEADLINE,
|
||||
MD.GRINDING_DEADLINE,
|
||||
MD.REQUIRED_QTY,
|
||||
MD.ORDER_QTY,
|
||||
MD.PRODUCTION_QTY,
|
||||
MD.STOCK_QTY,
|
||||
MD.SHORTAGE_QTY,
|
||||
-- 구매 정보
|
||||
MD.VENDOR,
|
||||
MD.UNIT_PRICE,
|
||||
MD.TOTAL_PRICE,
|
||||
MD.CURRENCY,
|
||||
MD.LEAD_TIME,
|
||||
MD.MIN_ORDER_QTY,
|
||||
-- 기타
|
||||
MD.STATUS,
|
||||
MD.WRITER,
|
||||
TO_CHAR(MD.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS REGDATE,
|
||||
MD.EDITER,
|
||||
CASE WHEN MD.EDIT_DATE IS NOT NULL THEN TO_CHAR(MD.EDIT_DATE, 'YYYY-MM-DD HH24:MI:SS') ELSE NULL END AS EDIT_DATE,
|
||||
MD.REMARK,
|
||||
-- E-BOM 호환을 위한 추가 컬럼
|
||||
MD.PART_OBJID AS LAST_PART_OBJID,
|
||||
MD.PART_OBJID AS BOM_LAST_PART_OBJID,
|
||||
NULL AS PARENT_PART_NO,
|
||||
NULL AS CONTRACT_OBJID,
|
||||
0 AS SUB_PART_CNT,
|
||||
CASE WHEN MD.LEVEL = 1 THEN MD.OBJID ELSE NULL END AS ROOT_OBJID,
|
||||
CASE WHEN MD.LEVEL = 1 THEN MD.OBJID ELSE NULL END AS SUB_ROOT_OBJID,
|
||||
1 AS LEAF,
|
||||
-- PART_MNG 테이블에서 추가 정보 조회
|
||||
PM.SPEC,
|
||||
PM.MATERIAL,
|
||||
PM.WEIGHT,
|
||||
PM.PART_TYPE,
|
||||
PM.REMARK,
|
||||
PM.REVISION,
|
||||
PM.MAKER,
|
||||
PM.THICKNESS,
|
||||
PM.WIDTH,
|
||||
PM.HEIGHT,
|
||||
PM.OUT_DIAMETER,
|
||||
PM.IN_DIAMETER,
|
||||
PM.LENGTH,
|
||||
PM.SOURCING_CODE,
|
||||
PM.HEAT_TREATMENT_HARDNESS,
|
||||
PM.HEAT_TREATMENT_METHOD,
|
||||
PM.SURFACE_TREATMENT,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = PM.UNIT) AS UNIT_TITLE,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = PM.PART_TYPE) AS PART_TYPE_TITLE,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE PM.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('3D_CAD')) AS CU01_CNT,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE PM.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_DRAWING_CAD')) AS CU02_CNT,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE PM.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS CU03_CNT
|
||||
FROM MBOM_DETAIL MD
|
||||
LEFT JOIN PART_MNG PM ON MD.PART_OBJID = PM.OBJID
|
||||
WHERE MD.MBOM_HEADER_OBJID = #{mbomHeaderObjid}
|
||||
AND MD.STATUS = 'ACTIVE'
|
||||
ORDER BY MD.SEQ
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user