1439 lines
44 KiB
Plaintext
1439 lines
44 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
|
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
|
|
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
|
<%@ page import="com.pms.common.utils.*"%>
|
|
<%@ page import="java.util.*" %>
|
|
<%@include file= "/init.jsp" %>
|
|
<%
|
|
String menuObjId = request.getParameter("menuObjId");
|
|
String menuName = CommonUtils.getMenuName(menuObjId, "반제품검사");
|
|
String OBJID = CommonUtils.checkNull(request.getParameter("OBJID"));
|
|
String INSPECTION_GROUP_ID = CommonUtils.checkNull(request.getParameter("INSPECTION_GROUP_ID"));
|
|
String WORK_ORDER_NO = CommonUtils.checkNull(request.getParameter("WORK_ORDER_NO"));
|
|
String actionType = CommonUtils.checkNull(request.getParameter("actionType"));
|
|
Map info = (Map)request.getAttribute("info");
|
|
|
|
// 로그인 사용자 정보
|
|
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
|
String loginUserName = CommonUtils.checkNull(person.getUserName());
|
|
String loginUserId = CommonUtils.checkNull(person.getUserId());
|
|
%>
|
|
<c:set var="now" value="<%=new java.util.Date()%>" />
|
|
<c:set var="today"><fmt:formatDate value="${now}" pattern="yyyy-MM-dd" /></c:set>
|
|
<!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>
|
|
body, html {
|
|
margin: 0;
|
|
padding: 0;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
font-family: 'Malgun Gothic', sans-serif;
|
|
}
|
|
|
|
.popup_wrap {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100vh;
|
|
}
|
|
|
|
.popup_header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 10px 15px;
|
|
background: #4a5568;
|
|
color: white;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
|
|
.popup_header h2 {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.popup_content {
|
|
display: flex;
|
|
flex: 1;
|
|
overflow: hidden;
|
|
gap: 10px;
|
|
padding: 10px;
|
|
}
|
|
|
|
/* 좌측 패널 (35%) */
|
|
.left_panel {
|
|
width: 35%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* 우측 패널 (65%) */
|
|
.right_panel {
|
|
width: 65%;
|
|
display: flex;
|
|
flex-direction: column;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.panel_header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 12px;
|
|
background: #4a5568;
|
|
color: white;
|
|
font-weight: bold;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.panel_header .btn_group {
|
|
display: flex;
|
|
gap: 5px;
|
|
}
|
|
|
|
.panel_body {
|
|
flex: 1;
|
|
overflow: auto;
|
|
}
|
|
|
|
.grid_wrap {
|
|
height: 100%;
|
|
}
|
|
|
|
/* 버튼 스타일 */
|
|
.btn_add {
|
|
padding: 4px 10px;
|
|
background: #48bb78;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.btn_add:hover {
|
|
background: #38a169;
|
|
}
|
|
|
|
.btn_del {
|
|
padding: 4px 10px;
|
|
background: #e53e3e;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.btn_del:hover {
|
|
background: #c53030;
|
|
}
|
|
|
|
.btn_save {
|
|
padding: 6px 20px;
|
|
background: #3182ce;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.btn_save:hover {
|
|
background: #2b6cb0;
|
|
}
|
|
|
|
.btn_edit {
|
|
padding: 6px 20px;
|
|
background: #e67e22;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.btn_edit:hover {
|
|
background: #d35400;
|
|
}
|
|
|
|
.btn_close {
|
|
padding: 6px 20px;
|
|
background: #718096;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.btn_close:hover {
|
|
background: #4a5568;
|
|
}
|
|
|
|
/* 헤더 버튼 영역 */
|
|
.header_btns {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
/* 선택된 행 하이라이트 */
|
|
.tabulator-row.tabulator-selected {
|
|
background-color: #e3f2fd !important;
|
|
}
|
|
|
|
/* 비활성화 안내 */
|
|
.disabled_notice {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
height: 100%;
|
|
color: #999;
|
|
font-size: 14px;
|
|
background: #f5f5f5;
|
|
}
|
|
|
|
/* Select2 스타일 */
|
|
.select2-container--open {
|
|
z-index: 9999 !important;
|
|
}
|
|
.select2-results__option {
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* 푸터 버튼 영역 */
|
|
.popup_footer {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 12px 15px;
|
|
background: #f5f5f5;
|
|
border-top: 1px solid #ddd;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="popup_wrap">
|
|
<!-- 헤더 -->
|
|
<div class="popup_header">
|
|
<h2><%=menuName%></h2>
|
|
</div>
|
|
|
|
<!-- 컨텐츠 영역 -->
|
|
<div class="popup_content">
|
|
<!-- 좌측: 양품 정보 (입고관리에서 가져옴) -->
|
|
<div class="left_panel">
|
|
<div class="panel_header">
|
|
<span>양품 정보</span>
|
|
<div class="btn_group">
|
|
<button type="button" class="btn_add" id="btnAddLeft">+ 행 추가</button>
|
|
<button type="button" class="btn_del" id="btnDelLeft">- 행 삭제</button>
|
|
<button type="button" class="btn_save" id="btnSaveLeft" style="margin-left:10px;">행잠금</button>
|
|
<button type="button" class="btn_edit" id="btnEditLeft" style="margin-left:5px;">잠금해제</button>
|
|
</div>
|
|
</div>
|
|
<div class="panel_body">
|
|
<div id="leftGrid" class="grid_wrap"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 우측: 불량 정보 -->
|
|
<div class="right_panel">
|
|
<div class="panel_header">
|
|
<span>불량 상세 <small style="font-weight:normal;opacity:0.8;">(좌측 행 선택 시 해당 불량 정보 표시)</small></span>
|
|
<div class="btn_group">
|
|
<button type="button" class="btn_add" id="btnAddRight">+ 행 추가</button>
|
|
<button type="button" class="btn_del" id="btnDelRight">- 행 삭제</button>
|
|
<!-- 불량상세 저장버튼 주석처리
|
|
<button type="button" class="btn_save" id="btnSaveRight" style="margin-left:10px;">저장</button>
|
|
-->
|
|
</div>
|
|
</div>
|
|
<div class="panel_body" id="rightPanelBody">
|
|
<div id="rightGrid" class="grid_wrap"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 하단 버튼 영역 -->
|
|
<div class="popup_btn_area" style="display:flex; justify-content:flex-end; gap:5px; padding:15px; background:#f5f5f5; border-top:1px solid #ddd;">
|
|
<input type="button" value="저장" class="plm_btns" id="btnSave">
|
|
<input type="button" value="닫기" class="plm_btns" id="btnClose">
|
|
</div>
|
|
</div>
|
|
|
|
<script type="text/javascript">
|
|
var leftGrid; // 좌측 그리드 (양품 정보)
|
|
var rightGrid; // 우측 그리드 (불량 정보)
|
|
var rowSeq = 0; // 행 시퀀스
|
|
|
|
// 현재 선택된 좌측 행 정보
|
|
var selectedLeftRowId = null;
|
|
var selectedLeftRowData = null;
|
|
|
|
// 전체 불량 데이터 (좌측 행별로 관리)
|
|
var allDefectData = {}; // { LEFT_ROW_ID: [불량데이터배열], ... }
|
|
|
|
// 로그인 사용자 정보
|
|
var loginUserName = "<%=loginUserName%>";
|
|
var loginUserId = "<%=loginUserId%>";
|
|
var today = "${today}";
|
|
|
|
// 현재 수정 중인 INSPECTION_GROUP_ID (수정 모드일 때 사용)
|
|
var currentInspectionGroupId = "<%=INSPECTION_GROUP_ID%>";
|
|
|
|
// 입고관리 데이터 (드롭박스용)
|
|
var deliveryDataList = [];
|
|
var modelNameList = [];
|
|
var partNoList = [];
|
|
var partNameList = [];
|
|
|
|
// 코드 목록 (드롭박스용)
|
|
var productTypeList = ["Machine", "A/S", "D/S", "B/S", "C/T", "A/C", "W/M", "기타"];
|
|
var defectTypeList = ["외관불량", "치수불량", "기능불량", "재료불량", "조립불량", "도장불량", "용접불량", "기타"];
|
|
var defectCauseList = ["작업자 실수", "설비 이상", "자재 불량", "설계 오류", "공정 이상", "환경 요인", "기타"];
|
|
var responsibleDeptList = ["사용자 정보(부서)", "구매", "생산기술", "제조1팀", "제조2팀", "제조3팀", "연구소", "외주업체", "품질"];
|
|
var processStatusList = ["Rework", "Scrap"];
|
|
var dispositionTypeList = ["특채", "수정", "폐기"];
|
|
|
|
$(document).ready(function(){
|
|
// 입고관리 데이터 로드
|
|
fn_loadDeliveryData();
|
|
|
|
// 그리드 초기화
|
|
fn_initLeftGrid();
|
|
fn_initRightGrid();
|
|
|
|
// 기존 데이터 로드 (수정 모드인 경우)
|
|
var objid = "<%=OBJID%>";
|
|
var inspectionGroupId = "<%=INSPECTION_GROUP_ID%>";
|
|
if((objid && objid != "") || (inspectionGroupId && inspectionGroupId != "")){
|
|
fn_loadData(objid, inspectionGroupId);
|
|
}
|
|
|
|
// 버튼 이벤트
|
|
$("#btnSave").click(fn_save); // 하단 저장: 전체 DB 저장
|
|
$("#btnSaveLeft").click(fn_saveSelectedLeft); // 좌측 행저장: 락 처리
|
|
$("#btnEditLeft").click(fn_editSelectedLeft); // 좌측 행수정: 락 해제
|
|
// $("#btnSaveRight").click(fn_saveSelectedRight); // 우측 저장: 주석처리
|
|
$("#btnClose").click(function(){ window.close(); });
|
|
$("#btnAddLeft").click(fn_addLeftRow);
|
|
$("#btnDelLeft").click(fn_delLeftRow);
|
|
$("#btnAddRight").click(fn_addRightRow);
|
|
$("#btnDelRight").click(fn_delRightRow);
|
|
});
|
|
|
|
// 입고관리 데이터 로드
|
|
function fn_loadDeliveryData(){
|
|
$.ajax({
|
|
url: "/purchaseOrder/getDeliveryListForDropdown.do",
|
|
type: "POST",
|
|
async: false,
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result && result.list){
|
|
deliveryDataList = result.list;
|
|
|
|
var modelSet = {}, partNoSet = {}, partNameSet = {};
|
|
deliveryDataList.forEach(function(item){
|
|
if(item.MODEL_NAME && item.MODEL_NAME != '') modelSet[item.MODEL_NAME] = item;
|
|
if(item.PART_NO && item.PART_NO != '') partNoSet[item.PART_NO] = item;
|
|
if(item.PART_NAME && item.PART_NAME != '') partNameSet[item.PART_NAME] = item;
|
|
});
|
|
|
|
modelNameList = Object.keys(modelSet);
|
|
partNoList = Object.keys(partNoSet);
|
|
partNameList = Object.keys(partNameSet);
|
|
|
|
console.log("입고관리 데이터 로드 완료:", deliveryDataList.length + "건");
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("입고관리 데이터 로드 실패:", error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Select2 에디터 생성 함수
|
|
function createSelect2Editor(options, allowClear) {
|
|
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");
|
|
option.value = opt;
|
|
option.text = opt;
|
|
if(opt == cellValue) option.selected = true;
|
|
select.appendChild(option);
|
|
});
|
|
|
|
container.appendChild(select);
|
|
|
|
onRendered(function() {
|
|
$(select).select2({
|
|
width: '100%',
|
|
dropdownAutoWidth: true,
|
|
placeholder: '선택',
|
|
allowClear: allowClear !== false,
|
|
dropdownParent: $('body')
|
|
});
|
|
|
|
$(select).on('select2:select select2:clear', function(e) {
|
|
success($(select).val() || '');
|
|
});
|
|
|
|
$(select).select2('open');
|
|
});
|
|
|
|
return container;
|
|
};
|
|
}
|
|
|
|
// 저장된 행인지 확인하는 함수 (편집 가능 여부 결정)
|
|
function isEditable(cell){
|
|
return !cell.getRow().getData().IS_SAVED;
|
|
}
|
|
|
|
// =====================================================
|
|
// 좌측 그리드 (양품 정보) 초기화
|
|
// =====================================================
|
|
function fn_initLeftGrid(){
|
|
var columns = [
|
|
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, width:30},
|
|
{title:"품명(모델명)", field:"MODEL_NAME", minWidth:120, headerSort:false,
|
|
editor: createSelect2Editor(modelNameList),
|
|
editable: isEditable
|
|
},
|
|
{title:"제품구분", field:"PRODUCT_TYPE", minWidth:80, headerSort:false,
|
|
editor: createSelect2Editor(productTypeList),
|
|
editable: isEditable
|
|
},
|
|
{title:"작업지시번호", field:"WORK_ORDER_NO", editor:"input", minWidth:100, headerSort:false,
|
|
editable: isEditable
|
|
},
|
|
{title:"부품품번", field:"PART_NO", minWidth:100, headerSort:false,
|
|
editor: createSelect2Editor(partNoList),
|
|
editable: isEditable,
|
|
cellEdited: function(cell){
|
|
var partNo = cell.getValue();
|
|
if(partNo){
|
|
var matched = deliveryDataList.find(function(d){ return d.PART_NO == partNo; });
|
|
if(matched){
|
|
cell.getRow().update({
|
|
PART_NAME: matched.PART_NAME || '',
|
|
RECEIPT_QTY: matched.RECEIPT_QTY || matched.DELIVERY_QTY || ''
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{title:"부품명", field:"PART_NAME", minWidth:120, headerSort:false,
|
|
editor: createSelect2Editor(partNameList),
|
|
editable: isEditable,
|
|
cellEdited: function(cell){
|
|
var partName = cell.getValue();
|
|
if(partName){
|
|
var matched = deliveryDataList.find(function(d){ return d.PART_NAME == partName; });
|
|
if(matched){
|
|
cell.getRow().update({
|
|
PART_NO: matched.PART_NO || '',
|
|
RECEIPT_QTY: matched.RECEIPT_QTY || matched.DELIVERY_QTY || ''
|
|
});
|
|
}
|
|
}
|
|
}
|
|
},
|
|
{title:"입고수량", field:"RECEIPT_QTY", editor:"number", hozAlign:"right", minWidth:70, headerSort:false,
|
|
editorParams:{min:0, step:1},
|
|
editable: isEditable,
|
|
formatter: function(cell){
|
|
var val = cell.getValue();
|
|
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
|
|
}
|
|
},
|
|
{title:"양품수량", field:"GOOD_QTY", editor:"number", hozAlign:"right", minWidth:70, headerSort:false,
|
|
editorParams:{min:0, step:1},
|
|
editable: isEditable,
|
|
formatter: function(cell){
|
|
var val = cell.getValue();
|
|
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
|
|
}
|
|
}
|
|
];
|
|
|
|
leftGrid = new Tabulator("#leftGrid", {
|
|
layout: "fitColumns",
|
|
height: "100%",
|
|
columns: columns,
|
|
data: [],
|
|
placeholder: "행을 추가해주세요.",
|
|
selectable: 1, // 단일 선택
|
|
// 저장된 행 시각적 표시
|
|
rowFormatter: function(row){
|
|
var data = row.getData();
|
|
if(data.IS_SAVED || data.IS_LOCKED === 'Y'){
|
|
row.getElement().style.backgroundColor = "#e8f5e9"; // 연한 초록색
|
|
row.getElement().style.color = "#555";
|
|
} else {
|
|
row.getElement().style.backgroundColor = ""; // 원래 색상
|
|
row.getElement().style.color = "";
|
|
}
|
|
}
|
|
});
|
|
|
|
// 행 선택 이벤트 - 해당 행의 불량 정보를 우측에 표시
|
|
leftGrid.on("rowSelected", function(row){
|
|
var data = row.getData();
|
|
selectedLeftRowId = data.ROW_ID;
|
|
selectedLeftRowData = data;
|
|
|
|
console.log("좌측 행 선택:", selectedLeftRowId);
|
|
|
|
// 해당 좌측 행에 연결된 불량 정보 우측에 표시
|
|
fn_showRightGridData(selectedLeftRowId);
|
|
fn_enableRightGrid();
|
|
});
|
|
|
|
// 행 선택 해제 이벤트
|
|
leftGrid.on("rowDeselected", function(row){
|
|
// 현재 우측 데이터를 저장
|
|
fn_saveRightGridData();
|
|
|
|
selectedLeftRowId = null;
|
|
selectedLeftRowData = null;
|
|
fn_clearRightGrid();
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 우측 그리드 (불량 정보) 초기화
|
|
// =====================================================
|
|
function fn_initRightGrid(){
|
|
var columns = [
|
|
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, width:30},
|
|
{title:"불량수량", field:"DEFECT_QTY", editor:"number", hozAlign:"right", minWidth:70, headerSort:false,
|
|
editorParams:{min:0, step:1},
|
|
editable: isEditable,
|
|
formatter: function(cell){
|
|
var val = cell.getValue();
|
|
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
|
|
}
|
|
},
|
|
{title:"불량유형", field:"DEFECT_TYPE", minWidth:85, headerSort:false,
|
|
editor: createSelect2Editor(defectTypeList),
|
|
editable: isEditable
|
|
},
|
|
{title:"불량원인", field:"DEFECT_CAUSE", minWidth:90, headerSort:false,
|
|
editor: createSelect2Editor(defectCauseList),
|
|
editable: isEditable
|
|
},
|
|
{title:"귀책부서", field:"RESPONSIBLE_DEPT", minWidth:80, headerSort:false,
|
|
editor: createSelect2Editor(responsibleDeptList),
|
|
editable: isEditable
|
|
},
|
|
{title:"부적합보고서", field:"NCR_FILE_CNT", minWidth:75, headerSort:false, hozAlign:"center",
|
|
formatter: fnc_subInfoValueFormatter,
|
|
cellClick: function(e, cell){
|
|
var objId = fnc_checkNull(cell.getData().OBJID);
|
|
if(objId){
|
|
fn_openNCRFilePopUp(objId);
|
|
} else {
|
|
Swal.fire({icon:'info', title:'알림', text:'행을 추가한 후 파일을 등록할 수 있습니다.'});
|
|
}
|
|
}
|
|
},
|
|
{title:"처리현황", field:"PROCESS_STATUS", minWidth:75, headerSort:false,
|
|
editor: createSelect2Editor(processStatusList),
|
|
editable: isEditable
|
|
},
|
|
{title:"이미지", field:"IMAGE_FILE_CNT", minWidth:60, headerSort:false, hozAlign:"center",
|
|
formatter: fnc_subInfoValueFormatter,
|
|
cellClick: function(e, cell){
|
|
var objId = fnc_checkNull(cell.getData().OBJID);
|
|
if(objId){
|
|
fn_openImageFilePopUp(objId);
|
|
} else {
|
|
Swal.fire({icon:'info', title:'알림', text:'행을 추가한 후 이미지를 등록할 수 있습니다.'});
|
|
}
|
|
}
|
|
},
|
|
{title:"검사일", field:"INSPECTION_DATE", minWidth:100, headerSort:false,
|
|
editor: "input",
|
|
editorParams: { elementAttributes: { type: "date" } },
|
|
editable: isEditable
|
|
},
|
|
{title:"검사자", field:"INSPECTOR", editor:"input", minWidth:70, headerSort:false, editable: isEditable},
|
|
{title:"처리결과", field:"DISPOSITION_TYPE", minWidth:75, headerSort:false,
|
|
editor: createSelect2Editor(dispositionTypeList),
|
|
editable: isEditable
|
|
},
|
|
{title:"검사성적서", field:"REPORT_FILE_CNT", minWidth:70, headerSort:false, hozAlign:"center",
|
|
formatter: fnc_subInfoValueFormatter,
|
|
cellClick: function(e, cell){
|
|
var objId = fnc_checkNull(cell.getData().OBJID);
|
|
if(objId){
|
|
fn_openReportFilePopUp(objId);
|
|
} else {
|
|
Swal.fire({icon:'info', title:'알림', text:'행을 추가한 후 파일을 등록할 수 있습니다.'});
|
|
}
|
|
}
|
|
},
|
|
{title:"비고", field:"REMARK", editor:"input", minWidth:100, headerSort:false, editable: isEditable}
|
|
];
|
|
|
|
rightGrid = new Tabulator("#rightGrid", {
|
|
layout: "fitColumns",
|
|
height: "100%",
|
|
columns: columns,
|
|
data: [],
|
|
placeholder: "좌측에서 양품 정보를 선택하세요.",
|
|
selectable: true,
|
|
// 저장된 행 시각적 표시
|
|
rowFormatter: function(row){
|
|
var data = row.getData();
|
|
if(data.IS_SAVED || data.IS_LOCKED === 'Y'){
|
|
row.getElement().style.backgroundColor = "#e8f5e9"; // 연한 초록색
|
|
row.getElement().style.color = "#555";
|
|
} else {
|
|
row.getElement().style.backgroundColor = ""; // 원래 색상
|
|
row.getElement().style.color = "";
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 우측 그리드에 데이터 표시 (선택된 좌측 행의 불량 데이터)
|
|
function fn_showRightGridData(leftRowId){
|
|
if(!leftRowId) return;
|
|
|
|
// 해당 좌측 행에 연결된 불량 데이터 표시
|
|
var defectData = allDefectData[leftRowId] || [];
|
|
rightGrid.setData(defectData);
|
|
|
|
console.log("우측 그리드 데이터 표시:", leftRowId, defectData.length + "건");
|
|
}
|
|
|
|
// 우측 그리드 데이터를 allDefectData에 저장
|
|
function fn_saveRightGridData(){
|
|
if(!selectedLeftRowId) return;
|
|
|
|
var currentData = rightGrid.getData();
|
|
allDefectData[selectedLeftRowId] = currentData;
|
|
|
|
console.log("우측 그리드 데이터 저장:", selectedLeftRowId, currentData.length + "건");
|
|
}
|
|
|
|
// 우측 그리드 비활성화
|
|
function fn_disableRightGrid(){
|
|
rightGrid.setData([]);
|
|
rightGrid.options.placeholder = "좌측에서 양품 정보를 선택하세요.";
|
|
$("#btnAddRight, #btnDelRight").prop('disabled', true);
|
|
}
|
|
|
|
// 우측 그리드 활성화
|
|
function fn_enableRightGrid(){
|
|
rightGrid.options.placeholder = "행을 추가해주세요.";
|
|
$("#btnAddRight, #btnDelRight").prop('disabled', false);
|
|
}
|
|
|
|
// 우측 그리드 초기화
|
|
function fn_clearRightGrid(){
|
|
rightGrid.setData([]);
|
|
rightGrid.options.placeholder = "좌측에서 양품 정보를 선택하세요.";
|
|
}
|
|
|
|
// =====================================================
|
|
// 좌측 그리드 행 추가
|
|
// =====================================================
|
|
function fn_addLeftRow(){
|
|
// 서버에서 OBJID 미리 생성
|
|
$.ajax({
|
|
url: "/quality/generateObjId.do",
|
|
type: "POST",
|
|
async: false,
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result){
|
|
rowSeq++;
|
|
var newObjId = result.OBJID;
|
|
|
|
leftGrid.addRow({
|
|
ROW_ID: newObjId, // OBJID를 ROW_ID로 사용
|
|
OBJID: newObjId, // 서버에서 생성한 실제 OBJID
|
|
MODEL_NAME: "",
|
|
PRODUCT_TYPE: "",
|
|
WORK_ORDER_NO: "",
|
|
PART_NO: "",
|
|
PART_NAME: "",
|
|
RECEIPT_QTY: 0,
|
|
GOOD_QTY: 0
|
|
});
|
|
|
|
// 새 행에 대한 불량 데이터 배열 초기화
|
|
allDefectData[newObjId] = [];
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("OBJID 생성 실패:", error);
|
|
Swal.fire({ icon: 'error', title: '오류', text: 'OBJID 생성에 실패했습니다.' });
|
|
}
|
|
});
|
|
}
|
|
|
|
// 좌측 그리드 행 삭제
|
|
function fn_delLeftRow(){
|
|
var selectedRows = leftGrid.getSelectedRows();
|
|
if(selectedRows.length == 0){
|
|
Swal.fire("삭제할 행을 선택해주세요.");
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
title: '삭제 확인',
|
|
text: '선택한 행과 연결된 불량 정보도 함께 삭제됩니다. 삭제하시겠습니까?',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#3085d6',
|
|
confirmButtonText: '삭제',
|
|
cancelButtonText: '취소'
|
|
}).then(function(result){
|
|
if(result.isConfirmed){
|
|
var objIdsToDelete = [];
|
|
|
|
selectedRows.forEach(function(row){
|
|
var rowData = row.getData();
|
|
var rowId = rowData.ROW_ID;
|
|
var objId = rowData.OBJID;
|
|
|
|
// DB에 저장된 데이터면 삭제 목록에 추가
|
|
if(objId && !String(objId).startsWith("NEW_")){
|
|
objIdsToDelete.push(objId);
|
|
}
|
|
|
|
// 해당 행의 불량 데이터도 삭제 목록에 추가
|
|
if(allDefectData[rowId]){
|
|
allDefectData[rowId].forEach(function(defect){
|
|
if(defect.OBJID && !String(defect.OBJID).startsWith("DEFECT_")){
|
|
objIdsToDelete.push(defect.OBJID);
|
|
}
|
|
});
|
|
delete allDefectData[rowId];
|
|
}
|
|
|
|
row.delete();
|
|
});
|
|
|
|
// DB에서 삭제
|
|
if(objIdsToDelete.length > 0){
|
|
$.ajax({
|
|
url: "/quality/deleteSemiProductInspection.do",
|
|
type: "POST",
|
|
data: { objIds: JSON.stringify(objIdsToDelete) },
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result){
|
|
console.log("DB 삭제 완료:", objIdsToDelete.length + "건");
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
} else {
|
|
console.error("DB 삭제 실패:", result.msg);
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("삭제 오류:", error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 우측 그리드 초기화
|
|
selectedLeftRowId = null;
|
|
selectedLeftRowData = null;
|
|
fn_clearRightGrid();
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 우측 그리드 행 추가
|
|
// =====================================================
|
|
function fn_addRightRow(){
|
|
if(!selectedLeftRowId){
|
|
Swal.fire("좌측에서 양품 정보를 먼저 선택하세요.");
|
|
return;
|
|
}
|
|
|
|
// 서버에서 OBJID 미리 생성 (첨부파일 등록을 위해)
|
|
$.ajax({
|
|
url: "/quality/generateObjId.do",
|
|
type: "POST",
|
|
async: false,
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result){
|
|
rowSeq++;
|
|
rightGrid.addRow({
|
|
OBJID: result.OBJID, // 서버에서 생성한 실제 OBJID
|
|
ROW_ID: result.OBJID, // ROW_ID도 동일하게
|
|
PARENT_ROW_ID: selectedLeftRowId,
|
|
DEFECT_QTY: 0,
|
|
DEFECT_TYPE: "",
|
|
DEFECT_CAUSE: "",
|
|
RESPONSIBLE_DEPT: "",
|
|
NCR_FILE_CNT: 0,
|
|
PROCESS_STATUS: "",
|
|
IMAGE_FILE_CNT: 0,
|
|
INSPECTION_DATE: today,
|
|
INSPECTOR: loginUserName,
|
|
DISPOSITION_TYPE: "",
|
|
REPORT_FILE_CNT: 0,
|
|
REMARK: ""
|
|
});
|
|
} else {
|
|
Swal.fire({icon:'error', title:'오류', text:'행 추가 중 오류가 발생했습니다.'});
|
|
}
|
|
},
|
|
error: function(){
|
|
Swal.fire({icon:'error', title:'오류', text:'서버 통신 오류가 발생했습니다.'});
|
|
}
|
|
});
|
|
}
|
|
|
|
// 우측 그리드 행 삭제
|
|
function fn_delRightRow(){
|
|
var selectedRows = rightGrid.getSelectedRows();
|
|
if(selectedRows.length == 0){
|
|
Swal.fire("삭제할 행을 선택해주세요.");
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
title: '삭제 확인',
|
|
text: '선택한 불량 정보를 삭제하시겠습니까?',
|
|
icon: 'warning',
|
|
showCancelButton: true,
|
|
confirmButtonColor: '#d33',
|
|
cancelButtonColor: '#3085d6',
|
|
confirmButtonText: '삭제',
|
|
cancelButtonText: '취소'
|
|
}).then(function(result){
|
|
if(result.isConfirmed){
|
|
var objIdsToDelete = [];
|
|
|
|
selectedRows.forEach(function(row){
|
|
var objId = row.getData().OBJID;
|
|
// DB에 저장된 데이터면 삭제 목록에 추가
|
|
if(objId && !String(objId).startsWith("DEFECT_")){
|
|
objIdsToDelete.push(objId);
|
|
}
|
|
row.delete();
|
|
});
|
|
|
|
// DB에서 삭제
|
|
if(objIdsToDelete.length > 0){
|
|
$.ajax({
|
|
url: "/quality/deleteSemiProductInspection.do",
|
|
type: "POST",
|
|
data: { objIds: JSON.stringify(objIdsToDelete) },
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result){
|
|
console.log("DB 삭제 완료:", objIdsToDelete.length + "건");
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
} else {
|
|
console.error("DB 삭제 실패:", result.msg);
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("삭제 오류:", error);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 파일 첨부 팝업
|
|
// =====================================================
|
|
// 이미지 파일 팝업
|
|
function fn_openImageFilePopUp(objId) {
|
|
var popup_width = 650;
|
|
var popup_height = 550;
|
|
var url = "/common/ImageRegistPopup.do?targetObjId=" + objId + "&docType=SEMI_INSPECTION_IMAGE&docTypeName=검사이미지";
|
|
var popup = window.open(url, "imageFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
|
|
fn_watchPopupClose(popup, objId, 'IMAGE_FILE_CNT', 'SEMI_INSPECTION_IMAGE');
|
|
}
|
|
|
|
// 부적합보고서 파일 팝업
|
|
function fn_openNCRFilePopUp(objId) {
|
|
var popup_width = 800;
|
|
var popup_height = 300;
|
|
var url = "/common/FileRegistPopup.do?targetObjId=" + objId + "&docType=SEMI_INSPECTION_NCR&docTypeName=부적합보고서";
|
|
var popup = window.open(url, "ncrFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
|
|
fn_watchPopupClose(popup, objId, 'NCR_FILE_CNT', 'SEMI_INSPECTION_NCR');
|
|
}
|
|
|
|
// 검사성적서 파일 팝업
|
|
function fn_openReportFilePopUp(objId) {
|
|
var popup_width = 800;
|
|
var popup_height = 300;
|
|
var url = "/common/FileRegistPopup.do?targetObjId=" + objId + "&docType=SEMI_INSPECTION_REPORT&docTypeName=검사성적서";
|
|
var popup = window.open(url, "reportFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
|
|
fn_watchPopupClose(popup, objId, 'REPORT_FILE_CNT', 'SEMI_INSPECTION_REPORT');
|
|
}
|
|
|
|
|
|
// 팝업 닫힘 감지 및 파일 카운트 업데이트
|
|
function fn_watchPopupClose(popup, objId, fieldName, docType) {
|
|
var checkPopup = setInterval(function() {
|
|
if(popup.closed) {
|
|
clearInterval(checkPopup);
|
|
fn_updateFileCnt(objId, fieldName, docType);
|
|
}
|
|
}, 500);
|
|
}
|
|
|
|
// 파일 카운트 업데이트
|
|
function fn_updateFileCnt(objId, fieldName, docType) {
|
|
$.ajax({
|
|
url: "/common/getFileList.do",
|
|
type: "POST",
|
|
data: { targetObjId: objId, docType: docType },
|
|
dataType: "json",
|
|
success: function(data) {
|
|
var cnt = data ? data.length : 0;
|
|
var rows = rightGrid.getRows();
|
|
rows.forEach(function(row) {
|
|
if(row.getData().OBJID === objId) {
|
|
var updateData = {};
|
|
updateData[fieldName] = cnt;
|
|
row.update(updateData);
|
|
}
|
|
});
|
|
// allDefectData도 업데이트
|
|
if(selectedLeftRowId && allDefectData[selectedLeftRowId]){
|
|
allDefectData[selectedLeftRowId].forEach(function(item){
|
|
if(item.OBJID === objId){
|
|
item[fieldName] = cnt;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 공통 콜백 함수 (파일 등록 팝업에서 호출)
|
|
function fnc_tabulCallbackFnc(objId, docType, columnField, fileCnt) {
|
|
var rows = rightGrid.getRows();
|
|
rows.forEach(function(row) {
|
|
if(row.getData().OBJID === objId) {
|
|
var updateData = {};
|
|
var targetField = (columnField && columnField !== '') ? columnField : 'FILE_CNT';
|
|
updateData[targetField] = fileCnt;
|
|
row.update(updateData);
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 기존 데이터 로드
|
|
// =====================================================
|
|
function fn_loadData(objid, inspectionGroupId){
|
|
$.ajax({
|
|
url: "/quality/getSemiProductInspectionDetail.do",
|
|
type: "POST",
|
|
data: { OBJID: objid, INSPECTION_GROUP_ID: inspectionGroupId },
|
|
dataType: "json",
|
|
success: function(result){
|
|
console.log("상세 데이터 로드:", result);
|
|
|
|
if(result && result.leftData && result.leftData.length > 0){
|
|
// 첫 번째 데이터에서 INSPECTION_GROUP_ID 가져오기 (수정 모드용)
|
|
if(result.leftData[0].INSPECTION_GROUP_ID){
|
|
currentInspectionGroupId = result.leftData[0].INSPECTION_GROUP_ID;
|
|
console.log("수정 모드 - INSPECTION_GROUP_ID:", currentInspectionGroupId);
|
|
}
|
|
|
|
// 좌측 데이터에 ROW_ID 부여 + IS_LOCKED='Y'인 행만 수정 불가
|
|
result.leftData.forEach(function(item, idx){
|
|
if(!item.ROW_ID) item.ROW_ID = item.OBJID || ("EXIST_" + (idx + 1));
|
|
// IS_LOCKED='Y'인 행만 수정 불가 (IS_SAVED=true)
|
|
item.IS_SAVED = (item.IS_LOCKED == 'Y');
|
|
rowSeq = Math.max(rowSeq, idx + 1);
|
|
});
|
|
leftGrid.setData(result.leftData);
|
|
|
|
// 불량 데이터를 좌측 행별로 분류
|
|
if(result.rightData && result.rightData.length > 0){
|
|
result.rightData.forEach(function(defect){
|
|
var parentId = defect.PARENT_ROW_ID || defect.ROW_ID;
|
|
|
|
// 좌측 데이터와 매칭 (같은 MODEL_NAME, PART_NO 기준)
|
|
var matchedLeft = result.leftData.find(function(left){
|
|
return left.MODEL_NAME == defect.MODEL_NAME && left.PART_NO == defect.PART_NO;
|
|
});
|
|
|
|
if(matchedLeft){
|
|
parentId = matchedLeft.ROW_ID;
|
|
}
|
|
|
|
if(!allDefectData[parentId]) allDefectData[parentId] = [];
|
|
defect.PARENT_ROW_ID = parentId;
|
|
// IS_LOCKED='Y'인 행만 수정 불가 (IS_SAVED=true)
|
|
defect.IS_SAVED = (defect.IS_LOCKED == 'Y');
|
|
allDefectData[parentId].push(defect);
|
|
});
|
|
}
|
|
|
|
console.log("불량 데이터 분류 완료:", allDefectData);
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("데이터 로드 오류:", error);
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 좌측 행수정: 락이 걸린 행을 수정 가능하도록 해제
|
|
// =====================================================
|
|
function fn_editSelectedLeft(){
|
|
console.log("===== fn_editSelectedLeft 호출됨 =====");
|
|
|
|
if(!selectedLeftRowId){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '수정할 양품 정보를 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
var selectedRow = leftGrid.getSelectedRows()[0];
|
|
if(!selectedRow){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '수정할 양품 정보를 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
var rowData = selectedRow.getData();
|
|
|
|
// 락이 걸려있지 않으면 이미 수정 가능 상태
|
|
if(rowData.IS_LOCKED !== 'Y' && !rowData.IS_SAVED){
|
|
Swal.fire({ icon: 'info', title: '알림', text: '이미 수정 가능한 상태입니다.' });
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
icon: 'question',
|
|
title: '행수정',
|
|
text: '선택한 행의 잠금을 해제하고 수정 가능하게 하시겠습니까?',
|
|
showCancelButton: true,
|
|
confirmButtonText: '확인',
|
|
cancelButtonText: '취소'
|
|
}).then((result) => {
|
|
if(result.isConfirmed){
|
|
var objId = rowData.OBJID;
|
|
|
|
// DB에 잠금 해제 요청
|
|
$.ajax({
|
|
url: "/quality/unlockSemiProductInspection.do",
|
|
type: "POST",
|
|
data: { objIds: JSON.stringify([objId]) },
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result){
|
|
// UI에서 잠금 해제
|
|
selectedRow.update({ IS_SAVED: false, IS_LOCKED: 'N' });
|
|
// 행 스타일 원래대로 (초록색 -> 기본색)
|
|
selectedRow.getElement().style.backgroundColor = "";
|
|
selectedRow.getElement().style.color = "";
|
|
Swal.fire({ icon: 'success', title: '완료', text: '잠금이 해제되어 수정 가능합니다.' });
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: '오류', text: result.msg || '잠금 해제에 실패했습니다.' });
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
Swal.fire({ icon: 'error', title: '오류', text: '잠금 해제 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 좌측 행 저장+잠금: 선택된 양품 행을 DB에 저장하고 수정 불가로 변경
|
|
// =====================================================
|
|
function fn_saveSelectedLeft(){
|
|
console.log("===== fn_saveSelectedLeft 호출됨 =====");
|
|
|
|
if(!selectedLeftRowId){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '저장할 양품 정보를 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
// 선택된 좌측 행
|
|
var selectedRow = leftGrid.getSelectedRows()[0];
|
|
if(!selectedRow){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '저장할 양품 정보를 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
var leftData = [selectedRow.getData()];
|
|
|
|
// DB에 저장 요청 (IS_LOCKED='Y'로 저장)
|
|
var paramData = {
|
|
leftData: JSON.stringify(leftData),
|
|
rightData: JSON.stringify([]),
|
|
INSPECTION_GROUP_ID: currentInspectionGroupId || "",
|
|
saveType: "left",
|
|
lockData: "Y" // 저장 시 잠금도 같이
|
|
};
|
|
|
|
console.log("좌측 저장+잠금 요청:", paramData);
|
|
|
|
$.ajax({
|
|
url: "/quality/saveSemiProductInspection.do",
|
|
type: "POST",
|
|
data: paramData,
|
|
dataType: "json",
|
|
success: function(result){
|
|
console.log("좌측 저장+잠금 응답:", result);
|
|
if(result.result == true || result.result == "true"){
|
|
// INSPECTION_GROUP_ID 업데이트
|
|
if(result.inspectionGroupId){
|
|
currentInspectionGroupId = result.inspectionGroupId;
|
|
console.log("INSPECTION_GROUP_ID 업데이트:", currentInspectionGroupId);
|
|
}
|
|
// UI에서도 수정 불가 처리
|
|
selectedRow.update({ IS_SAVED: true, IS_LOCKED: 'Y' });
|
|
console.log("행 업데이트 완료: IS_SAVED=true, IS_LOCKED=Y");
|
|
Swal.fire({ icon: 'success', title: '완료', text: '양품 정보가 저장 및 잠금 처리되었습니다.' });
|
|
// 부모 창 새로고침
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
} else {
|
|
console.log("좌측 저장 실패:", result.msg);
|
|
Swal.fire({ icon: 'error', title: '오류', text: result.msg || '저장에 실패했습니다.' });
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.log("좌측 저장 AJAX 오류:", xhr.responseText, status, error);
|
|
Swal.fire({ icon: 'error', title: '오류', text: '저장 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 우측 행 저장+잠금: 선택된 불량 행을 DB에 저장하고 수정 불가로 변경
|
|
// =====================================================
|
|
function fn_saveSelectedRight(){
|
|
if(!selectedLeftRowId){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '좌측에서 양품 정보를 먼저 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
// 선택된 우측 행만 가져오기
|
|
var selectedRightRows = rightGrid.getSelectedRows();
|
|
|
|
if(selectedRightRows.length == 0){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '저장할 불량 정보를 선택해주세요.' });
|
|
return;
|
|
}
|
|
|
|
// 선택된 좌측 행 (부모 정보 참조용)
|
|
var selectedLeftRow = leftGrid.getSelectedRows()[0];
|
|
var leftRowData = selectedLeftRow ? selectedLeftRow.getData() : {};
|
|
|
|
// 선택된 불량 데이터에 부모 정보 복사
|
|
var rightData = [];
|
|
selectedRightRows.forEach(function(row){
|
|
var defect = row.getData();
|
|
defect.PARENT_ROW_ID = selectedLeftRowId;
|
|
defect.MODEL_NAME = leftRowData.MODEL_NAME || '';
|
|
defect.PRODUCT_TYPE = leftRowData.PRODUCT_TYPE || '';
|
|
defect.WORK_ORDER_NO = leftRowData.WORK_ORDER_NO || '';
|
|
defect.PART_NO = leftRowData.PART_NO || '';
|
|
defect.PART_NAME = leftRowData.PART_NAME || '';
|
|
rightData.push(defect);
|
|
});
|
|
|
|
// DB에 저장 요청 (IS_LOCKED='Y'로 저장)
|
|
var paramData = {
|
|
leftData: JSON.stringify([]),
|
|
rightData: JSON.stringify(rightData),
|
|
INSPECTION_GROUP_ID: currentInspectionGroupId || "",
|
|
saveType: "right",
|
|
lockData: "Y" // 저장 시 잠금도 같이
|
|
};
|
|
|
|
$.ajax({
|
|
url: "/quality/saveSemiProductInspection.do",
|
|
type: "POST",
|
|
data: paramData,
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result == true || result.result == "true"){
|
|
// INSPECTION_GROUP_ID 업데이트
|
|
if(result.inspectionGroupId){
|
|
currentInspectionGroupId = result.inspectionGroupId;
|
|
}
|
|
// UI에서도 수정 불가 처리
|
|
selectedRightRows.forEach(function(row){
|
|
row.update({ IS_SAVED: true, IS_LOCKED: 'Y' });
|
|
});
|
|
Swal.fire({ icon: 'success', title: '완료', text: '불량 정보 ' + rightData.length + '건이 저장 및 잠금 처리되었습니다.' });
|
|
// 부모 창 새로고침
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
} else {
|
|
Swal.fire({ icon: 'error', title: '오류', text: result.msg || '저장에 실패했습니다.' });
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
Swal.fire({ icon: 'error', title: '오류', text: '저장 중 오류가 발생했습니다.' });
|
|
}
|
|
});
|
|
}
|
|
|
|
// =====================================================
|
|
// 실제 저장 실행 함수
|
|
// saveType: 'left' (좌측만), 'right' (우측만), 'all' (전체)
|
|
// =====================================================
|
|
function fn_doSave(leftData, rightData, confirmMsg, saveType){
|
|
saveType = saveType || 'all'; // 기본값은 전체 저장
|
|
|
|
Swal.fire({
|
|
icon: 'question',
|
|
title: '저장 확인',
|
|
text: confirmMsg,
|
|
showCancelButton: true,
|
|
confirmButtonText: '저장',
|
|
cancelButtonText: '취소'
|
|
}).then(function(result){
|
|
if(result.isConfirmed){
|
|
var paramData = {
|
|
leftData: JSON.stringify(leftData),
|
|
rightData: JSON.stringify(rightData),
|
|
INSPECTION_GROUP_ID: currentInspectionGroupId || "",
|
|
saveType: saveType // 저장 타입 추가
|
|
};
|
|
|
|
$.ajax({
|
|
url: "/quality/saveSemiProductInspection.do",
|
|
type: "POST",
|
|
data: paramData,
|
|
dataType: "json",
|
|
success: function(result){
|
|
if(result.result == true || result.result == "true"){
|
|
// 저장된 INSPECTION_GROUP_ID 유지 (다음 저장 시 같은 그룹으로 저장)
|
|
if(result.inspectionGroupId){
|
|
currentInspectionGroupId = result.inspectionGroupId;
|
|
console.log("INSPECTION_GROUP_ID 설정:", currentInspectionGroupId);
|
|
}
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: '저장 완료',
|
|
text: '저장되었습니다.'
|
|
}).then(function(){
|
|
// 부모 창 새로고침
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
// 데이터 다시 로드 (OBJID 동기화를 위해)
|
|
fn_loadData('', currentInspectionGroupId);
|
|
});
|
|
} else {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '저장 실패',
|
|
text: result.msg || '저장에 실패했습니다.'
|
|
});
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '오류 발생',
|
|
text: '저장 중 오류가 발생했습니다: ' + error
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// 저장된 행을 읽기 전용으로 마킹하는 함수
|
|
function fn_markAsSaved(savedLeftData, savedRightData){
|
|
// 좌측 그리드 행 업데이트
|
|
if(savedLeftData && savedLeftData.length > 0){
|
|
savedLeftData.forEach(function(leftItem){
|
|
var rowId = leftItem.ROW_ID;
|
|
var rows = leftGrid.getRows();
|
|
rows.forEach(function(row){
|
|
if(row.getData().ROW_ID === rowId){
|
|
row.update({ IS_SAVED: true });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// 우측 그리드 행 업데이트 (현재 표시된 데이터)
|
|
if(savedRightData && savedRightData.length > 0){
|
|
savedRightData.forEach(function(rightItem){
|
|
var objId = rightItem.OBJID;
|
|
var rows = rightGrid.getRows();
|
|
rows.forEach(function(row){
|
|
if(row.getData().OBJID === objId || row.getData().ROW_ID === rightItem.ROW_ID){
|
|
row.update({ IS_SAVED: true });
|
|
}
|
|
});
|
|
|
|
// allDefectData에도 IS_SAVED 설정
|
|
var parentId = rightItem.PARENT_ROW_ID;
|
|
if(parentId && allDefectData[parentId]){
|
|
allDefectData[parentId].forEach(function(defect){
|
|
if(defect.OBJID === objId || defect.ROW_ID === rightItem.ROW_ID){
|
|
defect.IS_SAVED = true;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// =====================================================
|
|
// 전체 저장
|
|
// =====================================================
|
|
function fn_save(){
|
|
try {
|
|
console.log("fn_save 시작");
|
|
|
|
// 현재 선택된 좌측 행이 있으면 우측 데이터를 allDefectData에 저장
|
|
if(selectedLeftRowId){
|
|
var currentRightData = rightGrid.getData();
|
|
allDefectData[selectedLeftRowId] = currentRightData;
|
|
console.log("현재 우측 그리드 데이터 저장:", selectedLeftRowId, currentRightData.length + "건");
|
|
}
|
|
|
|
var leftData = leftGrid.getData();
|
|
console.log("좌측 데이터 수:", leftData.length);
|
|
|
|
if(leftData.length == 0){
|
|
Swal.fire({ icon: 'warning', title: '알림', text: '저장할 양품 정보가 없습니다.' });
|
|
return;
|
|
}
|
|
|
|
// 모든 불량 데이터 수집 (allDefectData에서)
|
|
var rightData = [];
|
|
Object.keys(allDefectData).forEach(function(leftRowId){
|
|
var defects = allDefectData[leftRowId] || [];
|
|
defects.forEach(function(defect){
|
|
defect.PARENT_ROW_ID = leftRowId;
|
|
// 좌측 데이터에서 부모 정보 찾아서 복사
|
|
var parentLeft = leftData.find(function(l){ return l.ROW_ID == leftRowId; });
|
|
if(parentLeft){
|
|
defect.MODEL_NAME = parentLeft.MODEL_NAME || '';
|
|
defect.PRODUCT_TYPE = parentLeft.PRODUCT_TYPE || '';
|
|
defect.WORK_ORDER_NO = parentLeft.WORK_ORDER_NO || '';
|
|
defect.PART_NO = parentLeft.PART_NO || '';
|
|
defect.PART_NAME = parentLeft.PART_NAME || '';
|
|
}
|
|
rightData.push(defect);
|
|
});
|
|
});
|
|
console.log("우측 데이터 수:", rightData.length);
|
|
|
|
// 바로 저장 실행 (확인 팝업 생략)
|
|
console.log("저장 시작 - currentInspectionGroupId:", currentInspectionGroupId);
|
|
console.log("좌측 데이터:", leftData);
|
|
console.log("우측 데이터:", rightData);
|
|
|
|
var paramData = {
|
|
leftData: JSON.stringify(leftData),
|
|
rightData: JSON.stringify(rightData),
|
|
INSPECTION_GROUP_ID: currentInspectionGroupId || "",
|
|
saveType: "all"
|
|
};
|
|
|
|
$.ajax({
|
|
url: "/quality/saveSemiProductInspection.do",
|
|
type: "POST",
|
|
data: paramData,
|
|
dataType: "json",
|
|
success: function(result){
|
|
console.log("저장 결과:", result);
|
|
if(result.result == true || result.result == "true"){
|
|
// 저장된 INSPECTION_GROUP_ID 유지
|
|
if(result.inspectionGroupId){
|
|
currentInspectionGroupId = result.inspectionGroupId;
|
|
console.log("INSPECTION_GROUP_ID 설정:", currentInspectionGroupId);
|
|
}
|
|
|
|
// 저장된 행에 IS_SAVED 플래그 설정
|
|
fn_markAsSaved(leftData, rightData);
|
|
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: '저장 완료',
|
|
text: '저장되었습니다.'
|
|
}).then(function(){
|
|
if(window.opener && window.opener.fn_search){
|
|
window.opener.fn_search();
|
|
}
|
|
window.close();
|
|
});
|
|
} else {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '저장 실패',
|
|
text: result.msg || '저장에 실패했습니다.'
|
|
});
|
|
}
|
|
},
|
|
error: function(xhr, status, error){
|
|
console.error("AJAX 오류:", error);
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: '오류 발생',
|
|
text: '저장 중 오류가 발생했습니다: ' + error
|
|
});
|
|
}
|
|
});
|
|
} catch(e) {
|
|
console.error("fn_save 오류:", e);
|
|
alert("저장 중 오류 발생: " + e.message);
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|