1423 lines
41 KiB
Plaintext
1423 lines
41 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||
<%@ page import="com.pms.common.utils.*"%>
|
||
<%@ page import="java.util.*" %>
|
||
<%@include file= "/init.jsp" %>
|
||
<!DOCTYPE html>
|
||
<html>
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||
<title><%=Constants.SYSTEM_NAME%></title>
|
||
<link href="/css/tabulator/tabulator.min.css" rel="stylesheet">
|
||
<link href="/css/select2/select2.min.css" rel="stylesheet">
|
||
<script type="text/javascript" src="/js/tabulator/tabulator.min.js"></script>
|
||
<script type="text/javascript" src="/js/select2/select2.min.js"></script>
|
||
<style>
|
||
::-webkit-scrollbar {
|
||
width: 10px;
|
||
height: 15px;
|
||
}
|
||
#mBomTableWrap {
|
||
width: 99%;
|
||
margin: 10px auto;
|
||
overflow-x: auto;
|
||
overflow-y: hidden;
|
||
}
|
||
#mBomName {
|
||
margin-bottom: 10px;
|
||
font-weight: bold;
|
||
font-size: 16px;
|
||
}
|
||
body {
|
||
overflow: hidden;
|
||
}
|
||
/* 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;
|
||
}
|
||
</style>
|
||
<script>
|
||
var _tabulGrid;
|
||
var _lastValidData = [];
|
||
var selectedRowData = null; // 선택된 행 데이터
|
||
// 프로젝트 수주수량 (최상위 프레임에서 가져오기)
|
||
var projectQuantity = 1; // 기본값
|
||
// 총생산수량 (수주수량 + 추가생산수량)
|
||
var totalProductionQty = 1; // 기본값
|
||
|
||
// 소재 목록 전역 변수
|
||
var materialList = [];
|
||
|
||
// 공급업체(가공업체) 목록 전역 변수
|
||
var supplyVendorList = [];
|
||
|
||
$(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 사용");
|
||
}
|
||
// 총생산수량 가져오기 (수주수량 + 추가생산수량)
|
||
if(parent && parent.parent && parent.parent.TOTAL_PROD_QTY) {
|
||
totalProductionQty = parseFloat(parent.parent.TOTAL_PROD_QTY) || projectQuantity;
|
||
console.log("총생산수량:", totalProductionQty);
|
||
} else {
|
||
totalProductionQty = projectQuantity;
|
||
console.log("TOTAL_PROD_QTY를 찾을 수 없습니다. 수주수량 사용:", totalProductionQty);
|
||
}
|
||
} catch(e) {
|
||
console.log("프로젝트 수주수량 가져오기 실패:", e);
|
||
}
|
||
|
||
// 소재 목록 로드
|
||
fn_loadMaterialList();
|
||
|
||
// 공급업체(가공업체) 목록 로드
|
||
fn_loadSupplyVendorList();
|
||
|
||
// 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) {
|
||
// {id: CODE_ID(OBJID), text: NAME(CLIENT_NM)} 형태로 변환
|
||
supplyVendorList = data.map(function(item) {
|
||
return {
|
||
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 isCompleted = false;
|
||
|
||
function onComplete(val) {
|
||
if(isCompleted) return;
|
||
isCompleted = true;
|
||
$(document).off('mousedown.select2Editor');
|
||
try { $(select).select2('close'); $(select).select2('destroy'); } catch(e) {}
|
||
success(val);
|
||
}
|
||
|
||
// 빈 옵션 추가
|
||
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') {
|
||
option.value = opt.id || opt.value || '';
|
||
option.text = opt.text || opt.label || '';
|
||
} else {
|
||
option.value = opt;
|
||
option.text = opt;
|
||
}
|
||
if(option.value == cellValue) {
|
||
option.selected = true;
|
||
}
|
||
select.appendChild(option);
|
||
});
|
||
|
||
container.appendChild(select);
|
||
|
||
onRendered(function() {
|
||
$(select).select2({
|
||
width: '100%',
|
||
dropdownAutoWidth: true,
|
||
placeholder: '선택',
|
||
allowClear: true,
|
||
dropdownParent: $('body'),
|
||
templateResult: function(data) {
|
||
return data.text;
|
||
},
|
||
templateSelection: function(data) {
|
||
return data.text;
|
||
}
|
||
});
|
||
|
||
$(select).on('select2:select select2:clear', function(e) {
|
||
onComplete($(select).val() || null);
|
||
});
|
||
|
||
// 외부 클릭 시 mousedown 단계에서 먼저 에디터 종료 (클릭이 타겟 셀까지 전달되도록)
|
||
$(document).on('mousedown.select2Editor', function(e) {
|
||
if(!$(e.target).closest('.select2-container').length &&
|
||
!$(e.target).closest('.select2-dropdown').length) {
|
||
onComplete($(select).val() || null);
|
||
}
|
||
});
|
||
|
||
// Select2 열기
|
||
$(select).select2('open');
|
||
});
|
||
|
||
return container;
|
||
};
|
||
}
|
||
|
||
// 수준(LEVEL_X) 필드 생성 (파트 추가 시 사용)
|
||
var _maxLevel = ${empty MAXLEV ? 1 : MAXLEV};
|
||
function fn_setLevelFields(item) {
|
||
var level = parseInt(item.LEVEL) || 1;
|
||
for(var i = 1; i <= _maxLevel; i++) {
|
||
item['LEVEL_' + i] = (i === level) ? '*' : '';
|
||
}
|
||
return item;
|
||
}
|
||
|
||
// Tabulator 그리드 초기화
|
||
function fn_initGrid() {
|
||
var maxLevel = _maxLevel;
|
||
|
||
// 컬럼 정의
|
||
var columns = [];
|
||
|
||
// 라디오 버튼 컬럼 (E-BOM과 동일)
|
||
columns.push({
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 40,
|
||
title: '선택',
|
||
field: 'RADIO',
|
||
frozen: true,
|
||
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-PART_NAME="' + (rowData.PART_NAME || '') + '" ' +
|
||
'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 || '') + '" ' +
|
||
'data-CHILD_OBJID="' + (rowData.CHILD_OBJID || '') + '" ' +
|
||
'data-LEVEL="' + (rowData.LEVEL || 1) + '">';
|
||
}
|
||
});
|
||
|
||
// 수준 컬럼 그룹
|
||
var levelColumns = [];
|
||
for(var i = 1; i <= maxLevel; i++) {
|
||
levelColumns.push({
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 25,
|
||
minWidth: 25,
|
||
maxWidth: 25,
|
||
title: i,
|
||
field: 'LEVEL_' + i,
|
||
formatter: function(cell) {
|
||
return cell.getValue() === '*' ? '*' : '';
|
||
}
|
||
});
|
||
}
|
||
|
||
columns.push({
|
||
title: '수준',
|
||
headerHozAlign: 'center',
|
||
frozen: true,
|
||
columns: levelColumns
|
||
});
|
||
|
||
// 기본 정보 컬럼들 (E-BOM 정보)
|
||
columns.push(
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
widthGrow: 2,
|
||
title: '품번',
|
||
field: 'PART_NO',
|
||
frozen: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
widthGrow: 3,
|
||
title: '품명',
|
||
field: 'PART_NAME',
|
||
frozen: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 50,
|
||
title: '수량',
|
||
field: 'QTY_TEMP',
|
||
editor: 'number',
|
||
editorParams: { min: 0, step: 1, selectContents: true },
|
||
editable: function(cell) { var d = cell.getRow().getData(); return d._IS_ADDED === true && d._IS_SUB_PART !== true; },
|
||
formatter: function(cell) {
|
||
var d = cell.getRow().getData();
|
||
if(d._IS_ADDED === true && d._IS_SUB_PART !== true) {
|
||
cell.getElement().style.backgroundColor = '#FFF9C4';
|
||
cell.getElement().style.border = '1px solid #F9A825';
|
||
}
|
||
return cell.getValue();
|
||
},
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 80,
|
||
title: '항목 수량',
|
||
field: 'ITEM_QTY',
|
||
editor: 'number',
|
||
editorParams: { min: 0, step: 1, selectContents: true },
|
||
editable: function(cell) { var d = cell.getRow().getData(); return d._IS_ADDED === true && d._IS_SUB_PART !== true; },
|
||
formatter: function(cell) {
|
||
var d = cell.getRow().getData();
|
||
if(d._IS_ADDED === true && d._IS_SUB_PART !== true) {
|
||
cell.getElement().style.backgroundColor = '#FFF9C4';
|
||
cell.getElement().style.border = '1px solid #F9A825';
|
||
}
|
||
return cell.getValue();
|
||
},
|
||
visible: true
|
||
},
|
||
/* 주석처리: Rev, 규격, 제품구분, 상태 컬럼
|
||
{
|
||
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'
|
||
},
|
||
*/
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 50,
|
||
title: '3D',
|
||
field: 'CU01_CNT',
|
||
visible: true,
|
||
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,
|
||
title: '2D',
|
||
field: 'CU02_CNT',
|
||
visible: true,
|
||
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,
|
||
title: 'PDF',
|
||
field: 'CU03_CNT',
|
||
visible: true,
|
||
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
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 120,
|
||
title: '열처리경도',
|
||
field: 'HEAT_TREATMENT_HARDNESS',
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 120,
|
||
title: '열처리방법',
|
||
field: 'HEAT_TREATMENT_METHOD',
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 100,
|
||
title: '표면처리',
|
||
field: 'SURFACE_TREATMENT',
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 100,
|
||
title: '메이커',
|
||
field: 'MAKER',
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 70,
|
||
title: '범주이름',
|
||
field: 'PART_TYPE_TITLE',
|
||
visible: true
|
||
}
|
||
);
|
||
|
||
// 생산관리 컬럼 그룹
|
||
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();
|
||
if(data.SUPPLY_TYPE === '자급') {
|
||
// 자급 선택 시 소재 관련 필드 전부 초기화
|
||
row.update({
|
||
RAW_MATERIAL: null,
|
||
SIZE: null,
|
||
RAW_MATERIAL_NO: null,
|
||
REQUIRED_QTY: null,
|
||
ORDER_QTY: 0
|
||
});
|
||
} else {
|
||
// 사급 선택 시 소재소요량 재계산 (PART_UNIT_QTY / PART_UNIT_LENGTH)
|
||
var partUnitQty = parseFloat(data.PART_UNIT_QTY) || 0;
|
||
var partUnitLength = parseFloat(data.PART_UNIT_LENGTH) || 0;
|
||
var requiredQty = partUnitLength > 0 ? (partUnitQty / partUnitLength) : 0;
|
||
row.update({
|
||
RAW_MATERIAL: null,
|
||
SIZE: null,
|
||
RAW_MATERIAL_NO: null,
|
||
REQUIRED_QTY: requiredQty,
|
||
ORDER_QTY: 0
|
||
});
|
||
}
|
||
}
|
||
},
|
||
{
|
||
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() || '';
|
||
},
|
||
cellEdited: function(cell) {
|
||
// 소재 선택 시 사이즈 초기화 및 소재소요량/소재발주수량 재계산
|
||
var row = cell.getRow();
|
||
var data = row.getData();
|
||
|
||
// 소재소요량이 비어있으면 재계산 (SUPPLY_TYPE 변경 후 바로 소재 선택한 경우 대비)
|
||
if(!data.REQUIRED_QTY && data.REQUIRED_QTY !== 0) {
|
||
var partUnitQty = parseFloat(data.PART_UNIT_QTY) || 0;
|
||
var partUnitLength = parseFloat(data.PART_UNIT_LENGTH) || 0;
|
||
data.REQUIRED_QTY = partUnitLength > 0 ? (partUnitQty / partUnitLength) : 0;
|
||
}
|
||
|
||
data.ORDER_QTY = fn_calcOrderQty(data);
|
||
row.update({
|
||
SIZE: '',
|
||
RAW_MATERIAL_NO: '',
|
||
REQUIRED_QTY: data.REQUIRED_QTY,
|
||
ORDER_QTY: data.ORDER_QTY
|
||
});
|
||
row.reformat();
|
||
}
|
||
},
|
||
{
|
||
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;
|
||
},
|
||
formatter: function(cell) {
|
||
var data = cell.getRow().getData();
|
||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||
return cell.getValue() || '';
|
||
},
|
||
cellEdited: function(cell) {
|
||
// 사이즈 선택 시 소재품번 + UNIT_QTY/UNIT_LENGTH 자동 조회 후 소재소요량 재계산
|
||
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,
|
||
PART_NO: data.PART_NO
|
||
},
|
||
success: function(response) {
|
||
if(response && response.MATERIAL_PART_NO) {
|
||
// 소재의 UNIT_QTY, UNIT_LENGTH로 소재소요량 재계산
|
||
var partUnitQty = parseFloat(response.UNIT_QTY || response.unit_qty) || 0;
|
||
var partUnitLength = parseFloat(response.UNIT_LENGTH || response.unit_length) || 0;
|
||
var requiredQty = partUnitLength > 0 ? (partUnitQty / partUnitLength) : 0;
|
||
|
||
data.PART_UNIT_QTY = partUnitQty;
|
||
data.PART_UNIT_LENGTH = partUnitLength;
|
||
data.REQUIRED_QTY = requiredQty;
|
||
data.ORDER_QTY = fn_calcOrderQty(data);
|
||
|
||
row.update({
|
||
RAW_MATERIAL_NO: response.MATERIAL_PART_NO,
|
||
PART_UNIT_QTY: partUnitQty,
|
||
PART_UNIT_LENGTH: partUnitLength,
|
||
REQUIRED_QTY: requiredQty,
|
||
ORDER_QTY: data.ORDER_QTY
|
||
});
|
||
row.reformat();
|
||
|
||
console.log("소재 선택 - 품번:", response.MATERIAL_PART_NO, "UNIT_QTY:", partUnitQty, "UNIT_LENGTH:", partUnitLength, "소재소요량:", requiredQty);
|
||
}
|
||
}
|
||
});
|
||
} else if(!data.SIZE) {
|
||
// 규격 삭제 시 소재품번, 소재소요량, 소재발주수량 초기화
|
||
row.update({
|
||
RAW_MATERIAL_NO: '',
|
||
PART_UNIT_QTY: 0,
|
||
PART_UNIT_LENGTH: 0,
|
||
REQUIRED_QTY: 0,
|
||
ORDER_QTY: 0
|
||
});
|
||
row.reformat();
|
||
}
|
||
}
|
||
},
|
||
{
|
||
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',
|
||
width: 90,
|
||
title: '소재소요량',
|
||
field: 'REQUIRED_QTY',
|
||
editor: false,
|
||
formatter: function(cell) {
|
||
var data = cell.getRow().getData();
|
||
if(data.SUPPLY_TYPE === '자급') return '-';
|
||
|
||
// 전처리에서 계산된 값 표시 (소수 두번째 자리, ex: 0.20)
|
||
var value = parseFloat(data.REQUIRED_QTY) || 0;
|
||
return value.toFixed(2);
|
||
}
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'right',
|
||
width: 90,
|
||
title: '소재발주수량',
|
||
field: 'ORDER_QTY',
|
||
editor: false,
|
||
formatter: function(cell) {
|
||
var data = cell.getRow().getData();
|
||
|
||
if(data.SUPPLY_TYPE !== '사급' || !data.RAW_MATERIAL) {
|
||
return '-';
|
||
}
|
||
|
||
// 전처리에서 계산된 값 표시
|
||
var value = parseFloat(data.ORDER_QTY) || 0;
|
||
return value > 0 ? value.toLocaleString() : '0';
|
||
}
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'right',
|
||
width: 80,
|
||
title: '항목수량',
|
||
field: 'ITEM_QTY',
|
||
editor: false,
|
||
visible: true
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'right',
|
||
width: 80,
|
||
title: '제작수량',
|
||
field: 'PRODUCTION_QTY',
|
||
titleFormatter: function() { return '<span class="editable-header">제작수량</span>'; },
|
||
editor: 'number',
|
||
editorParams: {
|
||
min: 0,
|
||
step: 1,
|
||
selectContents: true // 편집 시 기존 값 전체 선택
|
||
},
|
||
formatter: function(cell) {
|
||
// 전처리에서 계산된 값 표시
|
||
var value = parseFloat(cell.getValue()) || 0;
|
||
return Number(value).toLocaleString();
|
||
}
|
||
},
|
||
{
|
||
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);
|
||
},
|
||
formatter: function(cell) {
|
||
var value = cell.getValue();
|
||
if(!value) return '';
|
||
|
||
// OBJID로 업체명 조회하여 표시
|
||
for(var i = 0; i < supplyVendorList.length; i++) {
|
||
if(supplyVendorList[i].id == value) {
|
||
return supplyVendorList[i].text;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'right',
|
||
width: 100,
|
||
title: '가공단가',
|
||
field: 'PROCESSING_UNIT_PRICE',
|
||
editor: false, // 구매쪽에서 입력
|
||
formatter: function(cell) {
|
||
var value = cell.getValue();
|
||
return value ? Number(value).toLocaleString() : '-';
|
||
}
|
||
},
|
||
/* 주석처리: 가공납기, 연삭납기 컬럼
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 100,
|
||
title: '가공납기',
|
||
field: 'PROCESSING_DEADLINE',
|
||
titleFormatter: function() { return '<span class="editable-header">가공납기</span>'; },
|
||
editor: 'date'
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'center',
|
||
width: 100,
|
||
title: '연삭납기',
|
||
field: 'GRINDING_DEADLINE',
|
||
titleFormatter: function() { return '<span class="editable-header">연삭납기</span>'; },
|
||
editor: 'date'
|
||
},
|
||
*/
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'left',
|
||
width: 150,
|
||
title: '공급업체',
|
||
field: 'VENDOR_NAME',
|
||
editor: false, // 구매쪽에서 입력
|
||
formatter: function(cell) {
|
||
return cell.getValue() || '-';
|
||
}
|
||
},
|
||
// 숨김 컬럼: 공급업체 코드 (저장 시 필요)
|
||
{
|
||
field: 'VENDOR',
|
||
visible: false
|
||
},
|
||
// 숨김 컬럼: 품의서 작성일 (저장 시 기존 값 유지)
|
||
{
|
||
field: 'PROPOSAL_DATE',
|
||
visible: false
|
||
},
|
||
// 숨김 컬럼: 순수량 (저장 시 기존 값 유지)
|
||
{
|
||
field: 'NET_QTY',
|
||
visible: false
|
||
},
|
||
// 숨김 컬럼: 발주수량 (저장 시 기존 값 유지)
|
||
{
|
||
field: 'PO_QTY',
|
||
visible: false
|
||
},
|
||
{
|
||
headerHozAlign: 'center',
|
||
hozAlign: 'right',
|
||
width: 100,
|
||
title: '소재단가',
|
||
field: 'UNIT_PRICE',
|
||
editor: false, // 구매쪽에서 입력
|
||
formatter: function(cell) {
|
||
var value = cell.getValue();
|
||
return value ? Number(value).toLocaleString() : '-';
|
||
}
|
||
}
|
||
// {
|
||
// headerHozAlign: 'center',
|
||
// hozAlign: 'right',
|
||
// width: 100,
|
||
// title: '금액',
|
||
// field: 'TOTAL_PRICE',
|
||
// editor: false,
|
||
// formatter: function(cell) {
|
||
// // 항목수량 × 단가
|
||
// var data = cell.getRow().getData();
|
||
// var itemQty = parseFloat(data.ITEM_QTY) || 0;
|
||
// var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
|
||
// 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);
|
||
|
||
// 대소문자 무관하게 필드값 가져오기 (PostgreSQL map은 소문자, UpperKeyMap은 대문자)
|
||
function fn_getField(row, upperName) {
|
||
return row[upperName] !== undefined ? row[upperName] : row[upperName.toLowerCase()];
|
||
}
|
||
|
||
// 데이터 전처리 함수: 필드명 정규화 + 소재소요량, 제작수량, 소재발주수량 미리 계산
|
||
function fn_preprocessBomData(dataList) {
|
||
if(!dataList || dataList.length === 0) return dataList;
|
||
|
||
dataList.forEach(function(row) {
|
||
// 필드명 정규화 (소문자 → 대문자 복사, 기존 대문자 유지)
|
||
if(row.part_unit_qty !== undefined && row.PART_UNIT_QTY === undefined) {
|
||
row.PART_UNIT_QTY = row.part_unit_qty;
|
||
}
|
||
if(row.part_unit_length !== undefined && row.PART_UNIT_LENGTH === undefined) {
|
||
row.PART_UNIT_LENGTH = row.part_unit_length;
|
||
}
|
||
|
||
// SUPPLY_TYPE 기본값
|
||
if(!row.SUPPLY_TYPE && !row.supply_type) {
|
||
row.SUPPLY_TYPE = '사급';
|
||
} else if(!row.SUPPLY_TYPE && row.supply_type) {
|
||
row.SUPPLY_TYPE = row.supply_type;
|
||
}
|
||
|
||
// 소재소요량: 저장된 값이 있으면 유지, 없으면 UNIT_QTY / UNIT_LENGTH로 계산
|
||
var savedRequiredQty = parseFloat(row.REQUIRED_QTY) || 0;
|
||
if(savedRequiredQty > 0) {
|
||
row.REQUIRED_QTY = savedRequiredQty;
|
||
} else {
|
||
var partUnitQty = parseFloat(row.PART_UNIT_QTY) || 0;
|
||
var partUnitLength = parseFloat(row.PART_UNIT_LENGTH) || 0;
|
||
row.REQUIRED_QTY = partUnitLength > 0 ? (partUnitQty / partUnitLength) : 0;
|
||
}
|
||
|
||
console.log("전처리 - PART_NO:", row.PART_NO, "소재품번:", row.RAW_MATERIAL_NO, "소재소요량(DB):", savedRequiredQty, "소재소요량(최종):", row.REQUIRED_QTY);
|
||
|
||
// 제작수량: 저장된 값이 있으면 유지, 없으면 항목수량 × 총생산수량으로 자동계산
|
||
var savedProdQty = parseFloat(row.PRODUCTION_QTY) || 0;
|
||
if(savedProdQty > 0) {
|
||
row.PRODUCTION_QTY = savedProdQty;
|
||
} else {
|
||
var itemQty = parseFloat(row.ITEM_QTY || row.item_qty) || 0;
|
||
row.PRODUCTION_QTY = itemQty * totalProductionQty;
|
||
}
|
||
|
||
// 소재발주수량: 저장된 값이 있으면 유지, 없으면 계산
|
||
var savedOrderQty = parseFloat(row.ORDER_QTY) || 0;
|
||
if(savedOrderQty > 0) {
|
||
row.ORDER_QTY = savedOrderQty;
|
||
} else {
|
||
row.ORDER_QTY = fn_calcOrderQty(row);
|
||
}
|
||
|
||
// 가공업체 기본값: 값이 없을 때만 적용 (저장된 값은 유지)
|
||
if(!row.PROCESSING_VENDOR) {
|
||
var partTypeTitle = row.PART_TYPE_TITLE || row.part_type_title || '';
|
||
if(partTypeTitle.indexOf('구매품') < 0) {
|
||
row.PROCESSING_VENDOR = '0000008377';
|
||
}
|
||
}
|
||
});
|
||
|
||
return dataList;
|
||
}
|
||
|
||
// 소재발주수량 계산 헬퍼
|
||
function fn_calcOrderQty(row) {
|
||
// 소재가 선택된 경우에만 계산 (사급이고 소재재질이 있을 때)
|
||
if(row.SUPPLY_TYPE !== '사급' || !row.RAW_MATERIAL) {
|
||
return 0;
|
||
}
|
||
|
||
var requiredQty = parseFloat(row.REQUIRED_QTY) || 0;
|
||
var productionQty = parseFloat(row.PRODUCTION_QTY) || 0;
|
||
|
||
if(requiredQty >= 1) {
|
||
// 소재소요량이 1 이상이면 제작수량과 동일
|
||
return productionQty;
|
||
} else if(requiredQty > 0 && requiredQty < 1) {
|
||
// 소재소요량이 1보다 작은 소수이면
|
||
// (unit_qty * 제작수량) / (unit_length - 50)
|
||
var unitQty = parseFloat(row.PART_UNIT_QTY) || 0;
|
||
var unitLength = parseFloat(row.PART_UNIT_LENGTH) || 0;
|
||
|
||
if(unitLength > 50) {
|
||
return Math.ceil((unitQty * productionQty) / (unitLength - 50));
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// 초기 데이터 전처리
|
||
bomTreeData = fn_preprocessBomData(bomTreeData);
|
||
|
||
// 모든 컬럼에 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",
|
||
height: "calc(100vh - 70px)",
|
||
columns: columns,
|
||
data: bomTreeData,
|
||
movableRows: true,
|
||
rowFormatter: function(row) {
|
||
var data = row.getData();
|
||
var level = data.LEVEL || 1;
|
||
row.getElement().classList.add('level-' + level);
|
||
}
|
||
});
|
||
|
||
// 초기 유효 데이터 저장
|
||
_lastValidData = bomTreeData.slice();
|
||
|
||
// 드래그 이동 후 같은 부모 내 이동만 허용, 하위 품목도 함께 이동
|
||
_tabulGrid.on("rowMoved", function(row) {
|
||
var movedData = row.getData();
|
||
var movedLevel = parseInt(movedData.LEVEL || 1);
|
||
var movedParent = movedData.PARENT_OBJID || '';
|
||
var movedChildObjid = movedData.CHILD_OBJID;
|
||
|
||
var currentData = _tabulGrid.getData();
|
||
var movedIndex = -1;
|
||
for(var i = 0; i < currentData.length; i++) {
|
||
if(currentData[i].CHILD_OBJID == movedChildObjid) { movedIndex = i; break; }
|
||
}
|
||
if(movedIndex < 0) return;
|
||
|
||
// 이동 위치 검증
|
||
var isValidMove = false;
|
||
if(movedIndex > 0) {
|
||
var prevData = currentData[movedIndex - 1];
|
||
var prevLevel = parseInt(prevData.LEVEL || 1);
|
||
if((prevLevel == movedLevel && prevData.PARENT_OBJID == movedParent) ||
|
||
(prevLevel == movedLevel - 1 && prevData.CHILD_OBJID == movedParent)) {
|
||
isValidMove = true;
|
||
}
|
||
} else {
|
||
if(!movedParent || movedParent == '') isValidMove = true;
|
||
}
|
||
|
||
if(!isValidMove) {
|
||
alert("같은 부모 내에서만 순서를 변경할 수 있습니다.");
|
||
_tabulGrid.setData(_lastValidData);
|
||
return;
|
||
}
|
||
|
||
// 이전 데이터에서 이동된 행의 하위 품목 추출
|
||
var subTree = [];
|
||
var oldData = _lastValidData;
|
||
var oldIndex = -1;
|
||
for(var i = 0; i < oldData.length; i++) {
|
||
if(oldData[i].CHILD_OBJID == movedChildObjid) { oldIndex = i; break; }
|
||
}
|
||
if(oldIndex >= 0) {
|
||
for(var j = oldIndex + 1; j < oldData.length; j++) {
|
||
if(parseInt(oldData[j].LEVEL || 1) > movedLevel) {
|
||
subTree.push(oldData[j]);
|
||
} else {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 하위 품목이 있으면 현재 데이터에서 제거 후 부모 뒤에 삽입
|
||
if(subTree.length > 0) {
|
||
var subChildObjids = {};
|
||
for(var k = 0; k < subTree.length; k++) {
|
||
subChildObjids[subTree[k].CHILD_OBJID] = true;
|
||
}
|
||
var filtered = [];
|
||
for(var i = 0; i < currentData.length; i++) {
|
||
if(!subChildObjids[currentData[i].CHILD_OBJID]) {
|
||
filtered.push(currentData[i]);
|
||
}
|
||
}
|
||
// 부모 위치 다시 찾기
|
||
var newParentIndex = -1;
|
||
for(var i = 0; i < filtered.length; i++) {
|
||
if(filtered[i].CHILD_OBJID == movedChildObjid) { newParentIndex = i; break; }
|
||
}
|
||
if(newParentIndex >= 0) {
|
||
for(var k = subTree.length - 1; k >= 0; k--) {
|
||
filtered.splice(newParentIndex + 1, 0, subTree[k]);
|
||
}
|
||
}
|
||
_tabulGrid.setData(filtered);
|
||
}
|
||
|
||
_lastValidData = _tabulGrid.getData();
|
||
});
|
||
|
||
// 행 클릭 시 선택 처리 이벤트
|
||
_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);
|
||
});
|
||
|
||
// 셀 편집 이벤트 등록 (SUPPLY_TYPE, RAW_MATERIAL은 컬럼 레벨 cellEdited에서 처리)
|
||
_tabulGrid.on("cellEdited", function(cell) {
|
||
var field = cell.getField();
|
||
var row = cell.getRow();
|
||
var data = row.getData();
|
||
|
||
// 제작수량 변경 시 소재발주수량 및 정미수량 재계산
|
||
if(field === 'PRODUCTION_QTY') {
|
||
// 수동 편집 플래그 설정
|
||
data._PRODUCTION_QTY_EDITED = true;
|
||
data.ORDER_QTY = fn_calcOrderQty(data);
|
||
var requiredQty = parseFloat(data.REQUIRED_QTY) || 0;
|
||
var productionQty = parseFloat(data.PRODUCTION_QTY) || 0;
|
||
var netQty = Math.ceil(requiredQty * productionQty);
|
||
row.update({
|
||
_PRODUCTION_QTY_EDITED: true,
|
||
ORDER_QTY: data.ORDER_QTY,
|
||
NET_QTY: netQty
|
||
});
|
||
row.reformat();
|
||
}
|
||
|
||
// 발주수량 또는 단가 변경 시 총단가 자동 계산
|
||
if(field === 'PO_QTY' || field === 'UNIT_PRICE') {
|
||
var poQty = parseFloat(data.PO_QTY) || 0;
|
||
var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
|
||
row.update({TOTAL_PRICE: poQty * unitPrice});
|
||
}
|
||
});
|
||
}
|
||
|
||
// 외부(mBomHeaderPopup)에서 파트 추가/삭제 후 호출하여 드래그 이동용 데이터 동기화
|
||
function fn_syncLastValidData() {
|
||
if(_tabulGrid) {
|
||
_lastValidData = _tabulGrid.getData().slice();
|
||
}
|
||
}
|
||
|
||
// 필터 적용 함수
|
||
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) {
|
||
console.log("fn_searchMbom (left) 호출됨:", searchParams);
|
||
|
||
$.ajax({
|
||
url: "/productionplanning/getMbomList.do",
|
||
method: 'post',
|
||
data: searchParams,
|
||
dataType: 'json',
|
||
success: function(data) {
|
||
console.log("M-BOM 조회 결과:", data);
|
||
if(data && data.list) {
|
||
console.log("데이터 개수:", data.list.length);
|
||
|
||
// 전처리 함수로 소재소요량, 제작수량, 소재발주수량 계산
|
||
var processedData = fn_preprocessBomData(data.list);
|
||
|
||
_tabulGrid.setData(processedData);
|
||
} else {
|
||
console.log("데이터 없음");
|
||
_tabulGrid.setData([]);
|
||
}
|
||
},
|
||
error: function(jqxhr, status, error){
|
||
console.error("M-BOM 조회 오류:", error);
|
||
console.error("응답:", jqxhr.responseText);
|
||
_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;
|
||
}
|
||
|
||
// 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 테이블에 필요한 모든 필드 포함)
|
||
// SEQ는 현재 화면 순서(인덱스) 기준으로 재계산
|
||
var mbomData = allData.map(function(row, index) {
|
||
return {
|
||
// BOM 구조 정보
|
||
parentObjid: row.PARENT_OBJID,
|
||
childObjid: row.CHILD_OBJID,
|
||
seq: index + 1,
|
||
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),
|
||
itemQty: toNumber(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),
|
||
|
||
// 구매 정보
|
||
vendor: row.VENDOR || row.VENDOR_PM, // 공급업체 코드/OBJID (기존 값 유지)
|
||
unitPrice: toNumber(row.UNIT_PRICE), // 소재단가
|
||
processingUnitPrice: toNumber(row.PROCESSING_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), // 발주수량
|
||
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
|
||
};
|
||
});
|
||
|
||
return mbomData;
|
||
}
|
||
|
||
// 엑셀 다운로드 (CSV 형식)
|
||
function fn_excel() {
|
||
if(!_tabulGrid) {
|
||
Swal.fire('데이터가 없습니다.');
|
||
return;
|
||
}
|
||
|
||
// 파일명 생성 (현재 날짜 포함)
|
||
var today = new Date();
|
||
var dateStr = today.getFullYear() + '_' +
|
||
String(today.getMonth() + 1).padStart(2, '0') + '_' +
|
||
String(today.getDate()).padStart(2, '0') + '_' +
|
||
String(today.getHours()).padStart(2, '0') + '_' +
|
||
String(today.getMinutes()).padStart(2, '0');
|
||
var fileName = 'M-BOM_' + dateStr + '.csv';
|
||
|
||
// Tabulator 내장 다운로드 기능 사용 (CSV)
|
||
_tabulGrid.download("csv", fileName, {
|
||
delimiter: ",",
|
||
bom: true // 한글 깨짐 방지 (UTF-8 BOM)
|
||
});
|
||
}
|
||
</script>
|
||
</head>
|
||
<body>
|
||
<div>
|
||
<input type="button" value="Excel Download" class="plm_btns" id="btnExcel" style="float:right; margin-right: 5px; margin-bottom: 5px;" onclick="fn_excel();">
|
||
</div>
|
||
<div id="mBomTableWrap"></div>
|
||
</body>
|
||
</html>
|
||
|