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

1150 lines
30 KiB
Plaintext
Raw Normal View History

2025-11-17 17:29:04 +09:00
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
<link href="/css/select2/select2.min.css" rel="stylesheet">
2025-11-17 17:29:04 +09:00
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
<script type="text/javascript" src="/js/select2/select2.min.js"></script>
2025-11-17 17:29:04 +09:00
<style>
::-webkit-scrollbar {
width: 10px;
height: 15px;
}
#mBomTableWrap {
width: 99%;
margin: 10px auto;
2025-11-18 14:37:36 +09:00
overflow-x: auto;
overflow-y: hidden;
2025-11-17 17:29:04 +09:00
}
#mBomName {
margin-bottom: 10px;
font-weight: bold;
font-size: 16px;
}
2025-11-18 14:37:36 +09:00
body {
overflow: hidden;
}
2025-11-17 17:29:04 +09:00
/* Tabulator 커스텀 스타일 */
.tabulator-row.level-1 { background-color: #fde9d9 !important; }
.tabulator-row.level-2 { background-color: #daeef3 !important; }
.tabulator-row.level-3 { background-color: #e4dfec !important; }
.tabulator-row.level-4 { background-color: #ebf1de !important; }
.tabulator-row.level-5 { background-color: #f2f2f2 !important; }
.tabulator-row.level-6 { background-color: #f2dcdb !important; }
.tabulator-row.level-7 { background-color: #eeece1 !important; }
.tabulator-row.level-8 { background-color: #dce6f1 !important; }
.tabulator-row.level-9 { background-color: #FFFFEB !important; }
.tabulator-row.level-10 { background-color: #ffffff !important; }
/* 편집 가능한 컬럼 헤더 스타일 */
.editable-header {
background-color: #fff9c4 !important;
}
/* 선택된 행 하이라이트 스타일 */
.tabulator-row.row-selected {
background-color: #b8daff !important;
border: 1px solid #007bff !important;
}
/* Select2 in Tabulator 스타일 */
.tabulator-cell .select2-container {
width: 100% !important;
}
.tabulator-cell .select2-container--default .select2-selection--single {
height: 100%;
border: none;
background: transparent;
}
.tabulator-cell .select2-container--default .select2-selection--single .select2-selection__rendered {
line-height: normal;
padding-left: 0;
}
.tabulator-cell .select2-container--default .select2-selection--single .select2-selection__arrow {
height: 100%;
}
.select2-container--open {
z-index: 9999 !important;
}
.select2-results__option {
font-size: 12px;
}
2025-11-17 17:29:04 +09:00
</style>
<script>
var _tabulGrid;
var selectedRowData = null; // 선택된 행 데이터
// 프로젝트 수주수량 (최상위 프레임에서 가져오기)
var projectQuantity = 1; // 기본값
2025-11-17 17:29:04 +09:00
// 소재 목록 전역 변수
var materialList = [];
// 공급업체(가공업체) 목록 전역 변수
var supplyVendorList = [];
2025-11-17 17:29:04 +09:00
$(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);
}
// 소재 목록 로드
fn_loadMaterialList();
// 공급업체(가공업체) 목록 로드
fn_loadSupplyVendorList();
2025-11-17 17:29:04 +09:00
// Tabulator 초기화
fn_initGrid();
});
// 소재 목록 로드
function fn_loadMaterialList() {
$.ajax({
url: '/admin/getMaterialList.do',
method: 'POST',
async: false,
success: function(response) {
if(response && response.list) {
// 중복 제거하여 소재 코드만 추출
var uniqueMaterials = {};
response.list.forEach(function(item) {
uniqueMaterials[item.MATERIAL_CODE] = true;
});
materialList = Object.keys(uniqueMaterials);
console.log("소재 목록 로드 완료:", materialList);
}
},
error: function() {
console.error("소재 목록 로드 실패");
materialList = [];
}
});
}
// 가공업체 목록 로드 (CLIENT_MNG 테이블)
function fn_loadSupplyVendorList() {
$.ajax({
url: '/common/getClientMngList.do',
method: 'POST',
async: false,
dataType: 'json',
success: function(data) {
if(data && data.length > 0) {
2025-12-12 15:09:58 +09:00
// {id: CODE_ID(OBJID), text: NAME(CLIENT_NM)} 형태로 변환
supplyVendorList = data.map(function(item) {
return {
2025-12-12 15:09:58 +09:00
id: item.CODE_ID,
text: item.NAME
};
});
console.log("가공업체 목록 로드 완료:", supplyVendorList.length + "개");
}
},
error: function() {
console.error("가공업체 목록 로드 실패");
supplyVendorList = [];
}
});
}
// Select2 커스텀 에디터 생성 함수
function createSelect2Editor(options) {
return function(cell, onRendered, success, cancel, editorParams) {
var cellValue = cell.getValue();
var container = document.createElement("span");
var select = document.createElement("select");
select.style.width = "100%";
// 빈 옵션 추가
var emptyOption = document.createElement("option");
emptyOption.value = "";
emptyOption.text = "선택";
select.appendChild(emptyOption);
// 옵션 추가
options.forEach(function(opt) {
var option = document.createElement("option");
if(typeof opt === 'object') {
2025-12-12 15:09:58 +09:00
option.value = opt.id || opt.value || '';
option.text = opt.text || opt.label || '';
} else {
option.value = opt;
option.text = opt;
}
2025-12-12 15:09:58 +09:00
if(option.value == cellValue) {
option.selected = true;
}
select.appendChild(option);
});
container.appendChild(select);
onRendered(function() {
$(select).select2({
width: '100%',
dropdownAutoWidth: true,
placeholder: '선택',
allowClear: true,
2025-12-12 15:09:58 +09:00
dropdownParent: $('body'), // 드롭다운이 셀 밖으로 나오도록
templateResult: function(data) {
return data.text;
},
templateSelection: function(data) {
return data.text;
}
});
$(select).on('select2:select select2:clear', function(e) {
success($(select).val() || null);
});
$(select).on('select2:close', function(e) {
// 포커스 잃었을 때 처리
});
// Select2 열기
$(select).select2('open');
});
return container;
};
}
2025-11-17 17:29:04 +09:00
// Tabulator 그리드 초기화
function fn_initGrid() {
var maxLevel = ${empty MAXLEV ? 1 : MAXLEV};
// 컬럼 정의
var columns = [];
2025-11-18 14:37:36 +09:00
// 라디오 버튼 컬럼 (E-BOM과 동일)
columns.push({
headerHozAlign: 'center',
hozAlign: 'center',
width: 40,
2025-11-18 14:37:36 +09:00
title: '선택',
field: 'RADIO',
frozen: true,
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var rowData = cell.getData();
return '<input type="radio" name="checkedPartNo" value="' + (rowData.CHILD_OBJID || '') + '" ' +
'data-OBJID="' + (rowData.OBJID || '') + '" ' +
'data-PART_NO="' + (rowData.PART_NO || '') + '" ' +
'data-PARENT_PART_NO="' + (rowData.PARENT_PART_NO || '') + '" ' +
'data-PART_NO_QTY="' + (rowData.LAST_PART_OBJID || '') + '" ' +
'data-PARENT_PARTS="' + (rowData.PARENT_PARTS || '') + '" ' +
'data-LAST_PART_OBJID="' + (rowData.LAST_PART_OBJID || rowData.BOM_LAST_PART_OBJID || '') + '" ' +
'data-PARENT_OBJID="' + (rowData.PARENT_OBJID || '') + '" ' +
'data-PART_OBJID="' + (rowData.PART_OBJID || '') + '" ' +
'data-BOM_LAST_PART_OBJID="' + (rowData.BOM_LAST_PART_OBJID || rowData.LAST_PART_OBJID || '') + '" ' +
2025-11-25 13:10:30 +09:00
'data-CHILD_OBJID="' + (rowData.CHILD_OBJID || '') + '" ' +
'data-LEVEL="' + (rowData.LEVEL || 1) + '" ' +
'data-PART_NO="' + (rowData.PART_NO || '') + '">';
2025-11-18 14:37:36 +09:00
}
});
2025-11-17 17:29:04 +09:00
// 수준 컬럼 그룹
var levelColumns = [];
for(var i = 1; i <= maxLevel; i++) {
levelColumns.push({
headerHozAlign: 'center',
hozAlign: 'center',
width: 25,
minWidth: 25,
maxWidth: 25,
2025-11-17 17:29:04 +09:00
title: i,
field: 'LEVEL_' + i,
formatter: function(cell) {
return cell.getValue() === '*' ? '*' : '';
}
});
}
columns.push({
title: '수준',
headerHozAlign: 'center',
frozen: true,
2025-11-17 17:29:04 +09:00
columns: levelColumns
});
2025-11-18 14:37:36 +09:00
// 기본 정보 컬럼들 (E-BOM 정보)
2025-11-17 17:29:04 +09:00
columns.push(
{
headerHozAlign: 'center',
hozAlign: 'left',
2025-11-18 14:37:36 +09:00
widthGrow: 2,
2025-11-17 17:29:04 +09:00
title: '품번',
field: 'PART_NO',
frozen: true
2025-11-17 17:29:04 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
2025-11-18 14:37:36 +09:00
widthGrow: 3,
2025-11-17 17:29:04 +09:00
title: '품명',
field: 'PART_NAME',
frozen: true
2025-11-17 17:29:04 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 50,
2025-11-17 17:29:04 +09:00
title: '수량',
2025-11-18 14:37:36 +09:00
field: 'QTY_TEMP',
visible: true
2025-11-17 17:29:04 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'center',
2025-11-18 14:37:36 +09:00
width: 80,
2025-11-17 17:29:04 +09:00
title: '항목 수량',
2025-11-18 14:37:36 +09:00
field: 'ITEM_QTY',
visible: true
2025-11-17 17:29:04 +09:00
},
2025-11-18 14:37:36 +09:00
/* 주석처리: Rev, 규격, 제품구분, 상태 컬럼
2025-11-17 17:29:04 +09:00
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 80,
title: 'Rev',
field: 'REVISION'
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '규격',
field: 'SPEC'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '제품구분',
field: 'PRODUCT_NAME'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '상태',
field: 'STATUS_NAME'
2025-11-18 14:37:36 +09:00
},
*/
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 50,
2025-11-18 14:37:36 +09:00
title: '3D',
field: 'CU01_CNT',
visible: true,
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var value = cell.getValue();
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
}
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 50,
2025-11-18 14:37:36 +09:00
title: '2D',
field: 'CU02_CNT',
visible: true,
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var value = cell.getValue();
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
}
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 60,
title: 'PDF',
field: 'CU03_CNT',
visible: true,
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var value = cell.getValue();
return value && value > 0 ? '<span class="file_icon"></span>' : '<span class="file_empty_icon"></span>';
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '재료',
field: 'MATERIAL',
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '열처리경도',
field: 'HEAT_TREATMENT_HARDNESS',
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '열처리방법',
field: 'HEAT_TREATMENT_METHOD',
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '표면처리',
field: 'SURFACE_TREATMENT',
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '메이커',
field: 'MAKER',
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '범주이름',
field: 'PART_TYPE_TITLE',
visible: true
2025-11-17 17:29:04 +09:00
}
);
2025-11-18 14:37:36 +09:00
// 생산관리 컬럼 그룹
columns.push({
title: '생산관리',
headerHozAlign: 'center',
columns: [
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 70,
title: '자급/사급',
field: 'SUPPLY_TYPE',
titleFormatter: function() { return '<span class="editable-header">자급/사급</span>'; },
editor: createSelect2Editor([{id: '자급', text: '자급'}, {id: '사급', text: '사급'}]),
cellEdited: function(cell) {
var row = cell.getRow();
var data = row.getData();
// 자급 선택 시 소재 관련 필드 초기화 (null로 설정하여 DB에서 NULL 처리)
if(data.SUPPLY_TYPE === '자급') {
row.update({
RAW_MATERIAL: null,
SIZE: null,
RAW_MATERIAL_NO: null,
REQUIRED_QTY: null
});
} else {
// 사급 선택 시 초기화
row.update({
RAW_MATERIAL: null,
SIZE: null,
RAW_MATERIAL_NO: null,
REQUIRED_QTY: null
});
2025-11-18 14:37:36 +09:00
}
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '소재',
field: 'RAW_MATERIAL',
titleFormatter: function() { return '<span class="editable-header">소재</span>'; },
editor: function(cell, onRendered, success, cancel, editorParams) {
// 소재 목록을 Select2용 형태로 변환
var options = materialList.map(function(m) { return {id: m, text: m}; });
return createSelect2Editor(options)(cell, onRendered, success, cancel, editorParams);
},
editable: function(cell) {
return cell.getRow().getData().SUPPLY_TYPE === '사급';
},
formatter: function(cell) {
var data = cell.getRow().getData();
if(data.SUPPLY_TYPE === '자급') return '-';
return cell.getValue() || '';
2025-11-18 14:37:36 +09:00
},
cellEdited: function(cell) {
// 소재 선택 시 사이즈 초기화
var row = cell.getRow();
row.update({
SIZE: '',
RAW_MATERIAL_NO: ''
});
}
},
2025-11-18 14:37:36 +09:00
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '사이즈',
field: 'SIZE',
titleFormatter: function() { return '<span class="editable-header">사이즈</span>'; },
editor: function(cell, onRendered, success, cancel, editorParams) {
// 선택된 소재에 따라 동적으로 사이즈 목록 로드
var data = cell.getRow().getData();
var materialCode = data.RAW_MATERIAL;
if(!materialCode) {
cancel();
return;
}
// 서버에서 해당 소재의 사이즈 목록 가져오기
var sizes = [];
$.ajax({
url: '/admin/getMaterialSizes.do',
method: 'POST',
data: {materialCode: materialCode},
async: false,
success: function(response) {
if(response && response.list) {
sizes = response.list.map(function(item) {
return {id: item.SIZE_SPEC, text: item.SIZE_SPEC};
});
}
}
});
return createSelect2Editor(sizes)(cell, onRendered, success, cancel, editorParams);
},
editable: function(cell) {
var data = cell.getRow().getData();
return data.SUPPLY_TYPE === '사급' && data.RAW_MATERIAL;
},
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var data = cell.getRow().getData();
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) {
// 서버에서 소재품번 조회
$.ajax({
url: '/admin/getMaterialPartNo.do',
method: 'POST',
data: {
materialCode: data.RAW_MATERIAL,
sizeSpec: data.SIZE
},
success: function(response) {
if(response && response.MATERIAL_PART_NO) {
row.update({RAW_MATERIAL_NO: response.MATERIAL_PART_NO});
}
}
});
}
2025-11-18 14:37:36 +09:00
}
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 120,
title: '소재품번',
field: 'RAW_MATERIAL_NO',
editor: false,
formatter: function(cell) {
var data = cell.getRow().getData();
if(data.SUPPLY_TYPE === '자급') return '-';
return cell.getValue() || '';
}
},
{
headerHozAlign: 'center',
hozAlign: 'right',
2025-11-18 14:37:36 +09:00
width: 100,
title: '소재소요량',
2025-11-18 14:37:36 +09:00
field: 'REQUIRED_QTY',
titleFormatter: function() { return '<span class="editable-header">소재소요량</span>'; },
editor: 'number',
editorParams: {
min: 0,
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';
}
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'right',
width: 120,
title: '소재발주수량',
2025-11-18 14:37:36 +09:00
field: 'ORDER_QTY',
editor: false,
formatter: function(cell) {
// 항목수량 × 프로젝트 수주수량 (수정 불가)
var data = cell.getRow().getData();
var itemQty = parseFloat(data.ITEM_QTY) || 0;
var orderQty = itemQty * projectQuantity;
// 실제 데이터에도 저장 (getMbomTreeData에서 사용)
cell.getRow().update({ORDER_QTY: orderQty}, false);
return orderQty.toLocaleString();
2025-11-18 14:37:36 +09:00
}
},
{
headerHozAlign: 'center',
hozAlign: 'right',
2025-11-18 14:37:36 +09:00
width: 80,
title: '항목수량',
field: 'ITEM_QTY',
editor: false,
visible: true
2025-11-18 14:37:36 +09:00
},
{
headerHozAlign: 'center',
hozAlign: 'right',
2025-11-18 14:37:36 +09:00
width: 100,
title: '제작수량',
field: 'PRODUCTION_QTY',
titleFormatter: function() { return '<span class="editable-header">제작수량</span>'; },
2025-11-18 14:37:36 +09:00
editor: 'number',
editorParams: {
min: 0,
step: 1
},
formatter: function(cell) {
2025-11-26 11:58:52 +09:00
// 저장된 값이 있으면 그대로 사용, 없으면 항목수량 × 수주수량으로 계산
var value = cell.getValue();
if(value === undefined || value === null || value === '' || value === 0) {
var data = cell.getRow().getData();
2025-11-26 11:58:52 +09:00
var itemQty = parseFloat(data.ITEM_QTY) || 0;
value = itemQty * projectQuantity;
// 실제 데이터에도 저장 (getMbomTreeData에서 사용)
cell.getRow().update({PRODUCTION_QTY: value}, false);
}
return Number(value).toLocaleString();
2025-11-18 14:37:36 +09:00
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '가공업체',
field: 'PROCESSING_VENDOR',
titleFormatter: function() { return '<span class="editable-header">가공업체</span>'; },
editor: function(cell, onRendered, success, cancel, editorParams) {
// 가공업체 목록 Select2 에디터
return createSelect2Editor(supplyVendorList)(cell, onRendered, success, cancel, editorParams);
2025-12-12 15:09:58 +09:00
},
formatter: function(cell) {
var value = cell.getValue();
// 저장된 값이 없으면 기본값 '5001'(RPS) 설정
if(value === undefined || value === null || value === '') {
value = '5001';
cell.getRow().update({PROCESSING_VENDOR: value}, false);
}
// OBJID로 업체명 조회하여 표시
for(var i = 0; i < supplyVendorList.length; i++) {
if(supplyVendorList[i].id == value) {
return supplyVendorList[i].text;
}
}
return value;
}
},
/* 주석처리: 가공납기, 연삭납기 컬럼
2025-11-18 14:37:36 +09:00
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '가공납기',
field: 'PROCESSING_DEADLINE',
titleFormatter: function() { return '<span class="editable-header">가공납기</span>'; },
2025-11-18 14:37:36 +09:00
editor: 'date'
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '연삭납기',
field: 'GRINDING_DEADLINE',
titleFormatter: function() { return '<span class="editable-header">연삭납기</span>'; },
2025-11-18 14:37:36 +09:00
editor: 'date'
},
*/
2025-11-18 14:37:36 +09:00
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '공급업체',
2025-11-27 14:52:51 +09:00
field: 'VENDOR_NAME',
editor: false, // 구매쪽에서 입력
formatter: function(cell) {
return cell.getValue() || '-';
}
2025-11-18 14:37:36 +09:00
},
2025-11-27 18:39:18 +09:00
// 숨김 컬럼: 공급업체 코드 (저장 시 필요)
{
field: 'VENDOR',
visible: false
},
// 숨김 컬럼: 품의서 작성일 (저장 시 기존 값 유지)
{
field: 'PROPOSAL_DATE',
visible: false
},
// 숨김 컬럼: 순수량 (저장 시 기존 값 유지)
{
field: 'NET_QTY',
visible: false
},
// 숨김 컬럼: 발주수량 (저장 시 기존 값 유지)
{
field: 'PO_QTY',
visible: false
},
2025-11-18 14:37:36 +09:00
{
headerHozAlign: 'center',
hozAlign: 'right',
width: 100,
title: '단가',
field: 'UNIT_PRICE',
editor: false, // 구매쪽에서 입력
2025-11-18 14:37:36 +09:00
formatter: function(cell) {
var value = cell.getValue();
return value ? Number(value).toLocaleString() : '-';
2025-11-18 14:37:36 +09:00
}
},
{
headerHozAlign: 'center',
hozAlign: 'right',
width: 100,
title: '금액',
2025-11-18 14:37:36 +09:00
field: 'TOTAL_PRICE',
editor: false,
formatter: function(cell) {
// 항목수량 × 단가
2025-11-18 14:37:36 +09:00
var data = cell.getRow().getData();
var itemQty = parseFloat(data.ITEM_QTY) || 0;
2025-11-18 14:37:36 +09:00
var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
var totalPrice = itemQty * unitPrice;
return totalPrice > 0 ? totalPrice.toLocaleString() : '-';
2025-11-18 14:37:36 +09:00
}
}
]
});
// 구매 컬럼 그룹 (제작수량 기준)
// 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();
// }
// }
// ]
// });
2025-11-18 14:37:36 +09:00
// 서버에서 전달받은 데이터 확인
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;
});
}
});
2025-11-17 17:29:04 +09:00
// Tabulator 생성
_tabulGrid = new Tabulator("#mBomTableWrap", {
2025-11-18 14:37:36 +09:00
layout: "fitData",
2025-11-25 15:15:20 +09:00
height: "calc(100vh - 70px)",
2025-11-17 17:29:04 +09:00
columns: columns,
2025-11-18 14:37:36 +09:00
data: bomTreeData,
2025-11-17 17:29:04 +09:00
rowFormatter: function(row) {
var data = row.getData();
var level = data.LEVEL || 1;
row.getElement().classList.add('level-' + level);
}
});
2025-11-18 14:37:36 +09:00
// 행 클릭 시 선택 처리 이벤트
_tabulGrid.on("rowClick", function(e, row) {
// 링크 클릭 시에는 기본 동작 유지
if($(e.target).is('a')) {
return;
}
// 기존 선택 해제
$('.tabulator-row.row-selected').removeClass('row-selected');
$('input[name=checkedPartNo]').prop('checked', false);
// 현재 행 선택
$(row.getElement()).addClass('row-selected');
var rowData = row.getData();
selectedRowData = rowData;
// 해당 행의 라디오 버튼 선택
$(row.getElement()).find('input[name=checkedPartNo]').prop('checked', true);
});
2025-11-18 14:37:36 +09:00
// 셀 편집 이벤트 등록
_tabulGrid.on("cellEdited", function(cell) {
var field = cell.getField();
var row = cell.getRow();
var data = row.getData();
// 지급/사급 변경 시 소재, 사이즈, 소요량 필드 재계산
if(field === 'SUPPLY_TYPE') {
if(data.SUPPLY_TYPE === '자급') {
// 자급인 경우 소재, 사이즈, 소요량 초기화 (null로 설정)
2025-11-18 14:37:36 +09:00
row.update({
RAW_MATERIAL: null,
SIZE: null,
REQUIRED_QTY: null
2025-11-18 14:37:36 +09:00
});
}
// 행 전체 재렌더링
row.reformat();
}
// 제작수량 변경 시 정미수량 자동 계산
if(field === 'PRODUCTION_QTY' || field === 'REQUIRED_QTY') {
var requiredQty = parseFloat(data.REQUIRED_QTY) || 0;
var productionQty = parseFloat(data.PRODUCTION_QTY) || 0;
var netQty = Math.ceil(requiredQty * productionQty);
row.update({NET_QTY: netQty});
}
// 발주수량 또는 단가 변경 시 총단가 자동 계산
if(field === 'PO_QTY' || field === 'UNIT_PRICE') {
var poQty = parseFloat(data.PO_QTY) || 0;
var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
var totalPrice = poQty * unitPrice;
row.update({TOTAL_PRICE: totalPrice});
}
// Q'ty 초기값 설정 (발주수량에서 가져오기)
if(field === 'ORDER_QTY' && !data.QTY) {
row.update({QTY: data.ORDER_QTY});
}
});
2025-11-17 17:29:04 +09:00
}
// 필터 적용 함수
function fn_applyFilter() {
var partNo = $("#filterPartNo").val().trim();
var partName = $("#filterPartName").val().trim();
if(!partNo && !partName) {
_tabulGrid.clearFilter();
return;
}
_tabulGrid.setFilter([
[
{field: "PART_NO", type: "like", value: partNo},
{field: "PART_NAME", type: "like", value: partName}
]
]);
}
// 필터 초기화 함수
function fn_resetFilter() {
$("#filterPartNo").val("");
$("#filterPartName").val("");
_tabulGrid.clearFilter();
}
// M-BOM 조회 (헤더 프레임에서 호출)
function fn_searchMbom(searchParams) {
2025-11-18 14:37:36 +09:00
console.log("fn_searchMbom (left) 호출됨:", searchParams);
2025-11-17 17:29:04 +09:00
$.ajax({
url: "/productionplanning/getMbomList.do",
method: 'post',
data: searchParams,
dataType: 'json',
success: function(data) {
2025-11-18 14:37:36 +09:00
console.log("M-BOM 조회 결과:", data);
2025-11-17 17:29:04 +09:00
if(data && data.list) {
2025-11-18 14:37:36 +09:00
console.log("데이터 개수:", data.list.length);
// ORDER_QTY, PRODUCTION_QTY 제거하여 formatter에서 재계산되도록
var processedData = data.list.map(function(item) {
delete item.ORDER_QTY;
delete item.PRODUCTION_QTY;
return item;
});
_tabulGrid.setData(processedData);
2025-11-17 17:29:04 +09:00
} else {
2025-11-18 14:37:36 +09:00
console.log("데이터 없음");
2025-11-17 17:29:04 +09:00
_tabulGrid.setData([]);
}
},
error: function(jqxhr, status, error){
console.error("M-BOM 조회 오류:", error);
2025-11-18 14:37:36 +09:00
console.error("응답:", jqxhr.responseText);
2025-11-17 17:29:04 +09:00
_tabulGrid.setData([]);
}
});
}
/* 주석처리: 가공납기/연삭납기 일괄 적용
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;
}
*/
// 선택된 행 데이터 가져오기 (Center 프레임에서 사용)
function getSelectedRowData() {
return selectedRowData;
}
2025-11-17 17:29:04 +09:00
// M-BOM 트리 데이터 수집 (저장용)
function getMbomTreeData() {
var allData = _tabulGrid.getData();
// 숫자 변환 헬퍼 함수 (빈 문자열, '-', 숫자가 아닌 값 → null)
function toNumber(value) {
if (value === null || value === undefined || value === '' || value === '-') return null;
var num = parseFloat(value);
return isNaN(num) ? null : num;
}
// 문자열 변환 헬퍼 함수 ('-' → null)
function toString(value) {
if (value === null || value === undefined || value === '-') return null;
return value;
}
// 데이터 구조 변환 (MBOM_DETAIL 테이블에 필요한 모든 필드 포함)
2025-11-17 17:29:04 +09:00
var mbomData = allData.map(function(row) {
return {
// 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: toString(row.RAW_MATERIAL), // 자급 시 '-' → null
rawMaterialSpec: toString(row.RAW_MATERIAL_SPEC),
rawMaterialSize: toString(row.SIZE), // 자급 시 '-' → null
rawMaterialPartNo: toString(row.RAW_MATERIAL_NO), // 자급 시 '-' → null
processingVendor: row.PROCESSING_VENDOR,
processingDeadline: row.PROCESSING_DEADLINE,
grindingDeadline: row.GRINDING_DEADLINE,
requiredQty: toNumber(row.REQUIRED_QTY), // 자급 시 '-' → null
orderQty: toNumber(row.ORDER_QTY),
productionQty: toNumber(row.PRODUCTION_QTY),
stockQty: toNumber(row.STOCK_QTY),
shortageQty: toNumber(row.SHORTAGE_QTY),
// 구매 정보
2025-11-27 18:39:18 +09:00
vendor: row.VENDOR || row.VENDOR_PM, // 공급업체 코드/OBJID (기존 값 유지)
unitPrice: toNumber(row.UNIT_PRICE),
// totalPrice 계산: 항목수량 × 단가
totalPrice: (function() {
var itemQty = parseFloat(row.ITEM_QTY) || 0;
var unitPrice = parseFloat(row.UNIT_PRICE) || 0;
return itemQty * unitPrice;
})(),
currency: row.CURRENCY,
leadTime: toNumber(row.LEAD_TIME),
minOrderQty: toNumber(row.MIN_ORDER_QTY),
netQty: toNumber(row.NET_QTY), // 순수량
poQty: toNumber(row.PO_QTY), // 발주수량
2025-11-27 18:39:18 +09:00
proposalDate: row.PROPOSAL_DATE, // 품의서 작성일 (기존 값 유지)
// 기타
status: row.STATUS,
remark: row.REMARK,
revision: row.REVISION,
spec: row.SPEC,
writer: row.WRITER,
editer: row.EDITER,
objid: row.OBJID
2025-11-17 17:29:04 +09:00
};
});
return mbomData;
}
</script>
</head>
<body>
2025-11-18 14:37:36 +09:00
<!-- <div id="mBomName">
2025-11-17 17:29:04 +09:00
<c:choose>
<c:when test="${not empty ebomInfo}">
E-BOM: ${ebomInfo.PART_NO} - ${ebomInfo.PART_NAME}
</c:when>
<c:otherwise>
할당된 E-BOM이 없습니다.
</c:otherwise>
</c:choose>
2025-11-18 14:37:36 +09:00
</div> -->
2025-11-17 17:29:04 +09:00
<div id="mBomTableWrap"></div>
</body>
</html>