Files
wace_plm/WebContent/WEB-INF/view/quality/semiProductInspectionFormPopUp.jsp

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>