Compare commits

...

2 Commits

Author SHA1 Message Date
b1ccbf7dac STMP 정보 변경 2025-12-11 15:38:28 +09:00
d5d0b8e0fb mbom 자급선택시 저장 오류 수정, 그리드 select2 적용 2025-12-11 15:18:47 +09:00
4 changed files with 264 additions and 131 deletions

View File

@@ -8,7 +8,9 @@
<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;
@@ -44,6 +46,29 @@ body {
.editable-header {
background-color: #fff9c4 !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;
@@ -53,6 +78,9 @@ var projectQuantity = 1; // 기본값
// 소재 목록 전역 변수
var materialList = [];
// 공급업체(가공업체) 목록 전역 변수
var supplyVendorList = [];
$(function(){
// 최상위 프레임(mBomPopupHeaderFs.jsp)에서 프로젝트 수주수량 가져오기
try {
@@ -69,6 +97,9 @@ $(function(){
// 소재 목록 로드
fn_loadMaterialList();
// 공급업체(가공업체) 목록 로드
fn_loadSupplyVendorList();
// Tabulator 초기화
fn_initGrid();
});
@@ -97,6 +128,89 @@ function fn_loadMaterialList() {
});
}
// 가공업체 목록 로드 (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_NAME, text: CODE_NAME} 형태로 변환 (Select2용)
supplyVendorList = data.map(function(item) {
return {
id: item.CODE_NAME,
text: item.CODE_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') {
option.value = opt.id || opt.value || opt;
option.text = opt.text || opt.label || opt;
} 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') // 드롭다운이 셀 밖으로 나오도록
});
$(select).on('select2:select select2:clear', function(e) {
success($(select).val() || null);
});
$(select).on('select2:close', function(e) {
// 포커스 잃었을 때 처리
});
// Select2 열기
$(select).select2('open');
});
return container;
};
}
// Tabulator 그리드 초기화
function fn_initGrid() {
var maxLevel = ${empty MAXLEV ? 1 : MAXLEV};
@@ -307,67 +421,65 @@ function fn_initGrid() {
title: '생산관리',
headerHozAlign: 'center',
columns: [
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
title: '자급/사급',
field: 'SUPPLY_TYPE',
titleFormatter: function() { return '<span class="editable-header">자급/사급</span>'; },
editor: 'list',
editorParams: {
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: ''
});
}
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 100,
title: '소재',
field: 'RAW_MATERIAL',
titleFormatter: function() { return '<span class="editable-header">소재</span>'; },
editor: 'list',
editorParams: {
values: materialList // 로드된 소재 목록 사용
},
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();
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 100,
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({
SIZE: '',
RAW_MATERIAL_NO: ''
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
});
}
}
},
{
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();
row.update({
SIZE: '',
RAW_MATERIAL_NO: ''
});
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
@@ -375,14 +487,14 @@ function fn_initGrid() {
title: '사이즈',
field: 'SIZE',
titleFormatter: function() { return '<span class="editable-header">사이즈</span>'; },
editor: 'list',
editorParams: function(cell) {
editor: function(cell, onRendered, success, cancel, editorParams) {
// 선택된 소재에 따라 동적으로 사이즈 목록 로드
var data = cell.getRow().getData();
var materialCode = data.RAW_MATERIAL;
if(!materialCode) {
return {values: []};
cancel();
return;
}
// 서버에서 해당 소재의 사이즈 목록 가져오기
@@ -395,13 +507,13 @@ function fn_initGrid() {
success: function(response) {
if(response && response.list) {
sizes = response.list.map(function(item) {
return item.SIZE_SPEC;
return {id: item.SIZE_SPEC, text: item.SIZE_SPEC};
});
}
}
});
return {values: sizes};
return createSelect2Editor(sizes)(cell, onRendered, success, cancel, editorParams);
},
editable: function(cell) {
var data = cell.getRow().getData();
@@ -525,18 +637,18 @@ function fn_initGrid() {
return Number(value).toLocaleString();
}
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 150,
title: '가공업체',
field: 'PROCESSING_VENDOR',
titleFormatter: function() { return '<span class="editable-header">가공업체</span>'; },
editor: 'list',
editorParams: {
values: ['업체A', '업체B', '업체C'] // TODO: 실제 가공업체 목록으로 교체
}
},
{
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);
}
},
/* 주석처리: 가공납기, 연삭납기 컬럼
{
headerHozAlign: 'center',
@@ -751,11 +863,11 @@ function fn_initGrid() {
// 지급/사급 변경 시 소재, 사이즈, 소요량 필드 재계산
if(field === 'SUPPLY_TYPE') {
if(data.SUPPLY_TYPE === '자급') {
// 자급인 경우 소재, 사이즈, 소요량 초기화
// 자급인 경우 소재, 사이즈, 소요량 초기화 (null로 설정)
row.update({
RAW_MATERIAL: '',
SIZE: 0,
REQUIRED_QTY: 0
RAW_MATERIAL: null,
SIZE: null,
REQUIRED_QTY: null
});
}
// 행 전체 재렌더링
@@ -885,13 +997,19 @@ function applyBulkDeadline(processingDeadline, grindingDeadline) {
function getMbomTreeData() {
var allData = _tabulGrid.getData();
// 숫자 변환 헬퍼 함수
// 숫자 변환 헬퍼 함수 (빈 문자열, '-', 숫자가 아닌 값 → null)
function toNumber(value) {
if (value === null || value === undefined || value === '') return null;
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 테이블에 필요한 모든 필드 포함)
var mbomData = allData.map(function(row) {
return {
@@ -906,29 +1024,29 @@ function getMbomTreeData() {
partNo: row.PART_NO,
partName: row.PART_NAME,
// 수량 정보 (원본 값 유지 - null/undefined만 변환)
// 수량 정보
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,
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: row.REQUIRED_QTY, // 원본 값 그대로
orderQty: row.ORDER_QTY, // 원본 값 그대로
productionQty: row.PRODUCTION_QTY, // 원본 값 그대로
stockQty: row.STOCK_QTY, // 원본 값 그대로
shortageQty: row.SHORTAGE_QTY, // 원본 값 그대로
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: row.UNIT_PRICE, // 원본 값 그대로
unitPrice: toNumber(row.UNIT_PRICE),
// totalPrice 계산: 항목수량 × 단가
totalPrice: (function() {
var itemQty = parseFloat(row.ITEM_QTY) || 0;
@@ -936,10 +1054,10 @@ function getMbomTreeData() {
return itemQty * unitPrice;
})(),
currency: row.CURRENCY,
leadTime: row.LEAD_TIME, // 원본 값 그대로
minOrderQty: row.MIN_ORDER_QTY, // 원본 값 그대로
netQty: row.NET_QTY, // 순수량 추가
poQty: row.PO_QTY, // 발주수량 추가
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, // 품의서 작성일 (기존 값 유지)
// 기타

View File

@@ -469,13 +469,16 @@ public class Constants {
public static final String FILE_PATH = FILE_STORAGE+"\\MAIL";
public static final String CHARSET = SYSTEM_CHARSET;
/* SMTP 메일정보 */
public static final String SMTP_USER = "admin@wsse.co.kr";//SMTP USER ID [mail.gdnsi.com]
//public static final String SMTP_USER = "admin@wsse.co.kr";
//public static final String SMTP_USER_PW = "admin123!@#";
public static final String SMTP_USER_PW = "admin123!@#"; //SMTP USER PASSWORD
public static final String SMTP_HOST = "smtps.hiworks.com"; //SMTP HOST
public static final int SMTP_PORT = 465; //SMTP PORT
/* SMTP 메일정보 - RPS */
public static final String SMTP_USER = "sales@rps-korea.com";//SMTP USER ID (전체 이메일 주소)
public static final String SMTP_USER_PW = "rpstech6125!!"; //SMTP USER PASSWORD
public static final String SMTP_HOST = "wblock.rps-korea.com"; //SMTP HOST (gw.rps-korea.com IP)
//public static final String SMTP_HOST = "220.123.92.226"; //SMTP HOST (gw.rps-korea.com IP)
public static final int SMTP_PORT = 25; //SMTP PORT (SSL 사용안함)
// public static final String SMTP_USER = "admin@wsse.co.kr";//SMTP USER ID [mail.gdnsi.com]
// public static final String SMTP_USER_PW = "admin123!@#"; //SMTP USER PASSWORD
// public static final String SMTP_HOST = "smtps.hiworks.com"; //SMTP HOST
// public static final int SMTP_PORT = 465; //SMTP PORT
}
//스마트공장 사후관리시스템 로그 수집 API key

View File

@@ -599,20 +599,16 @@ public class MailUtil {
Properties prop = new Properties();
prop.put("mail.smtp.host", Constants.Mail.SMTP_HOST);
prop.put("mail.smtp.port", Constants.Mail.SMTP_PORT);
//보안연결 SSL과 관련된 설정
prop.put("mail.smtp.auth" , "true");
prop.put("mail.smtp.starttls.enable" , "true");
prop.put("mail.smtps.checkserveridentity", "true");
prop.put("mail.smtps.ssl.trust" , "*");
prop.put("mail.debug" , "true");
prop.put("mail.smtp.socketFactory.class" , "javax.net.ssl.SSLSocketFactory");
prop.put("mail.smtp.ssl.enable" , "true");
prop.put("mail.smtp.socketFactory.port" , Constants.Mail.SMTP_PORT);
//GMAIL추가
prop.put("mail.smtp.ssl.trust" , "smtp.gmail.com");
prop.put("mail.transport.protocol", "smtp");
prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
prop.put("mail.transport.protocol" , "smtp");
// RPS 메일서버용 (SSL 사용안함, 포트 25)
prop.put("mail.smtp.ssl.enable" , "false");
prop.put("mail.smtp.starttls.enable" , "false");
// hiworks SSL 설정 (포트 465)
//prop.put("mail.smtp.ssl.enable" , "true");
//prop.put("mail.smtp.socketFactory.class" , "javax.net.ssl.SSLSocketFactory");
//prop.put("mail.smtp.socketFactory.port" , Constants.Mail.SMTP_PORT);
Session mailSession = Session.getDefaultInstance(prop, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
@@ -827,23 +823,22 @@ public class MailUtil {
if(toEmailList == null){ return false; }
//◆◆◆ 1. mail session ◆◆◆
Properties prop = new Properties();
prop.put("mail.smtp.host", Constants.Mail.SMTP_HOST);
prop.put("mail.smtp.port", Constants.Mail.SMTP_PORT);
prop.put("mail.smtp.auth" , "true");
prop.put("mail.smtp.starttls.enable" , "true");
prop.put("mail.smtps.checkserveridentity", "true");
prop.put("mail.smtps.ssl.trust" , "*");
prop.put("mail.debug" , "true");
prop.put("mail.smtp.socketFactory.class" , "javax.net.ssl.SSLSocketFactory");
prop.put("mail.smtp.ssl.enable" , "true");
prop.put("mail.smtp.socketFactory.port" , Constants.Mail.SMTP_PORT);
prop.put("mail.smtp.ssl.trust" , "smtp.gmail.com");
prop.put("mail.transport.protocol", "smtp");
prop.put("mail.smtp.ssl.protocols", "TLSv1.2");
Session mailSession = Session.getDefaultInstance(prop, new javax.mail.Authenticator() {
//◆◆◆ 1. mail session ◆◆◆
Properties prop = new Properties();
prop.put("mail.smtp.host", Constants.Mail.SMTP_HOST);
prop.put("mail.smtp.port", Constants.Mail.SMTP_PORT);
prop.put("mail.smtp.auth" , "true");
prop.put("mail.debug" , "true");
prop.put("mail.transport.protocol" , "smtp");
// RPS 메일서버용 (SSL 사용안함, 포트 25)
prop.put("mail.smtp.ssl.enable" , "false");
prop.put("mail.smtp.starttls.enable" , "false");
// hiworks SSL 설정 (포트 465)
//prop.put("mail.smtp.ssl.enable" , "true");
//prop.put("mail.smtp.socketFactory.class" , "javax.net.ssl.SSLSocketFactory");
//prop.put("mail.smtp.socketFactory.port" , Constants.Mail.SMTP_PORT);
Session mailSession = Session.getDefaultInstance(prop, new javax.mail.Authenticator() {
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(Constants.Mail.SMTP_USER, Constants.Mail.SMTP_USER_PW);
}

View File

@@ -1164,6 +1164,23 @@ public class CommonController {
return resultList;
}
/**
* 일반거래처(CLIENT_MNG) 목록 조회 - 가공업체 셀렉트박스용
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/common/getClientMngList.do")
public List<Map<String,Object>> getClientMngList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
List<Map<String,Object>> resultList = new ArrayList<>();
try {
resultList = commonService.selectList("common.getClientMngSupplySelect", request, paramMap);
} catch(Exception e) {
e.printStackTrace();
}
return resultList;
}
/**
* 상담지역2 조회