diff --git a/WebContent/WEB-INF/view/quality/incomingInspectionProgressList.jsp b/WebContent/WEB-INF/view/quality/incomingInspectionProgressList.jsp index d1240fb..461c10d 100644 --- a/WebContent/WEB-INF/view/quality/incomingInspectionProgressList.jsp +++ b/WebContent/WEB-INF/view/quality/incomingInspectionProgressList.jsp @@ -86,6 +86,8 @@ $(document).ready(function(){ // 컬럼: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 입고결과, 요청일, 요청자, 검사자, 검사일, 검사결과 var columns = [ + {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1, title:'검사일', field:'INSPECTION_DATE'}, + {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'검사자', field:'INSPECTOR_NAME'}, {headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'품의서 No', field:'PROPOSAL_NO' // formatter:fnc_createGridAnchorTag, // cellClick:function(e, cell){ @@ -103,7 +105,7 @@ var columns = [ {headerHozAlign:'center', hozAlign:'center', minWidth:130, widthGrow:1, title:'프로젝트번호', field:'PROJECT_NO'}, {headerHozAlign:'center', hozAlign:'center', minWidth:110, widthGrow:1, title:'제품구분', field:'PRODUCT_NAME'}, {headerHozAlign:'center', hozAlign:'left', minWidth:120, widthGrow:2, title:'품번', field:'PART_NO'}, - {headerHozAlign:'center', hozAlign:'left', minWidth:150, widthGrow:3, title:'품명', field:'PART_NAME'}, + {headerHozAlign:'center', hozAlign:'left', minWidth:130, widthGrow:3, title:'품명', field:'PART_NAME'}, {headerHozAlign:'center', hozAlign:'left', minWidth:130, widthGrow:2, title:'공급업체', field:'PARTNER_NAME'}, {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'입고결과', field:'DELIVERY_STATUS', formatter:fnc_createGridAnchorTag, @@ -115,28 +117,26 @@ var columns = [ }, // {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1, title:'요청일', field:'REQUEST_DATE'}, // {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'요청자', field:'REQUEST_USER_NAME'}, - {headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1, title:'검사일', field:'INSPECTION_DATE'}, - {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'검사자', field:'INSPECTOR_NAME'}, - {headerHozAlign : 'center', hozAlign : 'center', minWidth : 100, widthGrow : 1, title : '업체성적서', field : 'INSPECTION_FILE_CNT', - formatter:fnc_subInfoValueFormatter, - cellClick:function(e, cell){ - var objid = fnc_checkNull(cell.getData().OBJID); - fn_FileRegist(objid,"INSPECTION_FILE","검사성적서"); - } - }, - {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'검사결과', field:'INSPECTION_RESULT', + {headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'검사현황', field:'INSPECTION_RESULT', formatter: function(cell, formatterParams, onRendered){ var val = fnc_checkNull(cell.getValue()); - if(val === 'NG') return 'NG'; - if(val === 'OK') return 'OK'; - if(val === '검사중') return '검사중'; + // 처리결과 기준: 전부 입력 = 완료, 일부만 입력 = 진행중, 없으면 미검사 + if(val === '완료') return '완료'; + if(val === '진행중') return '진행중'; return '미검사'; }, cellClick:function(e, cell){ var objId = fnc_checkNull(cell.getData().OBJID); fn_inspectionPopUp(objId); } - } + }, + {headerHozAlign : 'center', hozAlign : 'center', minWidth : 100, widthGrow : 1, title : '업체성적서', field : 'INSPECTION_FILE_CNT', + formatter:fnc_subInfoValueFormatter, + cellClick:function(e, cell){ + var objid = fnc_checkNull(cell.getData().OBJID); + fn_FileRegist(objid,"INSPECTION_FILE","검사성적서"); + } + } ]; // 조회 diff --git a/WebContent/WEB-INF/view/quality/incomingInspectionProgressPopUp.jsp b/WebContent/WEB-INF/view/quality/incomingInspectionProgressPopUp.jsp index f7e55bf..c3032f7 100644 --- a/WebContent/WEB-INF/view/quality/incomingInspectionProgressPopUp.jsp +++ b/WebContent/WEB-INF/view/quality/incomingInspectionProgressPopUp.jsp @@ -136,15 +136,15 @@ var _DEFECT_TYPE_LIST = []; // 처리현황 목록 var _ACTION_STATUS_LIST = [ {"CODE": "", "NAME": "선택"}, - {"CODE": "Rework", "NAME": "Rework"}, - {"CODE": "Scrap", "NAME": "Scrap"} + {"CODE": "Rework", "NAME": "수정"}, + {"CODE": "Scrap", "NAME": "폐기"} ]; // 처리결과 목록 var _ACTION_RESULT_LIST = [ {"CODE": "", "NAME": "선택"}, - {"CODE": "수정", "NAME": "수정"}, + {"CODE": "수정", "NAME": "수정완료"}, {"CODE": "폐기", "NAME": "폐기"}, - {"CODE": "특채", "NAME": "특채"} + {"CODE": "특채", "NAME": "특채완료"} ]; // 검사자 목록 var _INSPECTOR_LIST = []; @@ -208,8 +208,8 @@ $(document).ready(function(){ // ===================================================== function fn_initLeftGrid() { var columns = [ - {formatter:"rowSelection", titleFormatter:"rowSelection", headerHozAlign:"center", hozAlign:"center", headerSort:false, width:30}, - {title:'품번', field:'PART_NO', headerHozAlign:'center', hozAlign:'left', width:150, + {formatter:"rowSelection", titleFormatter:"rowSelection", headerHozAlign:"center", hozAlign:"center", headerSort:false, width:30, frozen: true}, + {title:'품번', field:'PART_NO', headerHozAlign:'center', hozAlign:'left', width:150, frozen: true, formatter: fnc_createGridAnchorTag, cellClick: function(e, cell) { // 품번 클릭 시 품목상세 팝업 @@ -218,18 +218,52 @@ function fn_initLeftGrid() { } } }, - {title:'품명', field:'PART_NAME', headerHozAlign:'center', hozAlign:'left', width:150}, + {title:'품명', field:'PART_NAME', headerHozAlign:'center', hozAlign:'left', width:150, frozen: true}, {title:'입고일', field:'DELIVERY_DATE', headerHozAlign:'center', hozAlign:'center', width:90}, {title:'입고수량', field:'DELIVERY_QTY', headerHozAlign:'center', hozAlign:'right', width:90, formatter:"money", formatterParams:{thousand:",", precision:false} }, - {title:'검사여부', field:'INSPECTION_YN', headerHozAlign:'center', hozAlign:'center', width:70, + {title:'검사여부', field:'INSPECTION_YN', headerHozAlign:'center', hozAlign:'center', width:80, formatter: function(cell) { var val = cell.getValue(); if(val === '검사') return '검사'; if(val === '스킵') return '스킵'; return val; } + }, + {title:'검사수량', field:'INSPECTION_QTY', headerHozAlign:'center', hozAlign:'right', width:85, + editor:"number", + editorParams: {min:0, step:1}, + formatter: function(cell) { + var val = cell.getValue(); + if(val === null || val === '' || val === undefined) return ''; + return parseInt(val).toLocaleString(); + } + }, + {title:'불량수량', field:'DEFECT_QTY_SUM', headerHozAlign:'center', hozAlign:'right', width:80, + formatter: function(cell) { + var val = cell.getValue(); + if(val === null || val === '' || val === undefined) return '0'; + return parseInt(val).toLocaleString(); + } + }, + {title:'불량율', field:'LEFT_DEFECT_RATE', headerHozAlign:'center', hozAlign:'right', width:70, + formatter: function(cell) { + var val = cell.getValue(); + if(val === null || val === '' || val === undefined) return ''; + return val + '%'; + } + }, + {title:'검사성적서', field:'INSPECTION_FILE_CNT', headerHozAlign:'center', hozAlign:'center', width:90, + formatter: fnc_subInfoValueFormatter, + cellClick: function(e, cell) { + var objId = fnc_checkNull(cell.getData().INSPECTION_DETAIL_OBJID); + if(objId) { + fn_openInspectionFilePopUp(objId); + } else { + Swal.fire("먼저 저장 후 파일을 등록할 수 있습니다."); + } + } } ]; @@ -272,7 +306,75 @@ function fn_initLeftGrid() { // 셀 편집 이벤트 leftGrid.on("cellEdited", function(cell) { - cell.getData().GRID_STATUS = 'U'; + var row = cell.getRow(); + var data = row.getData(); + var field = cell.getField(); + + // 검사수량 변경 시 불량율 재계산 + if (field === 'INSPECTION_QTY') { + fn_calcLeftGridDefectRate(row); + } + + data.GRID_STATUS = 'U'; + }); +} + +// 좌측 그리드 불량율 계산 (불량수량합계 / 입고수량 * 100) +function fn_calcLeftGridDefectRate(row) { + var data = row.getData(); + var deliveryQty = parseInt(data.DELIVERY_QTY) || 0; + var defectQtySum = parseInt(data.DEFECT_QTY_SUM) || 0; + + if (deliveryQty > 0) { + var rate = (defectQtySum / deliveryQty * 100).toFixed(2); + row.update({"LEFT_DEFECT_RATE": rate}); + } else { + row.update({"LEFT_DEFECT_RATE": ""}); + } +} + +// 검사성적서 파일 팝업 +function fn_openInspectionFilePopUp(objId) { + var popup_width = 800; + var popup_height = 300; + var params = "?targetObjId=" + objId + "&docType=INSPECTION_REPORT&docTypeName=검사성적서"; + var url = "/common/FileRegistPopup.do" + params; + window.open(url, "inspectionFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes"); +} + +// 좌측 그리드 불량수량 합계/불량율 실시간 업데이트 (우측 그리드 변경 시) +function fn_updateLeftGridDefectSum() { + if(!selectedRowData) return; + + // 우측 그리드의 불량수량 합계 계산 + var rightGridData = rightGrid.getData(); + var defectQtySum = 0; + rightGridData.forEach(function(row) { + if(row.GRID_STATUS !== 'D') { // 삭제 표시된 행 제외 + defectQtySum += parseInt(row.DEFECT_QTY) || 0; + } + }); + + // 좌측 그리드에서 현재 선택된 행 찾기 + var leftRows = leftGrid.getRows(); + leftRows.forEach(function(row) { + var data = row.getData(); + if(data.INSPECTION_DETAIL_OBJID === selectedDetailObjid) { + // 불량수량 합계 업데이트 + row.update({"DEFECT_QTY_SUM": defectQtySum}); + + // 불량율 재계산 (불량수량합계 / 입고수량 * 100) + var deliveryQty = parseInt(data.DELIVERY_QTY) || 0; + if(deliveryQty > 0) { + var rate = (defectQtySum / deliveryQty * 100).toFixed(2); + row.update({"LEFT_DEFECT_RATE": rate}); + } else { + row.update({"LEFT_DEFECT_RATE": ""}); + } + + // 변경 표시 + data.GRID_STATUS = 'U'; + } }); } @@ -334,29 +436,30 @@ function fn_initRightGrid() { }, editorParams: {valueId:"CODE", labelId:"NAME", values:_ACTION_STATUS_LIST} }, - {title:'검사수량', field:'INSPECTION_QTY', headerHozAlign:'center', hozAlign:'right', width:80, - editor:"input", - formatter: function(cell) { - var val = cell.getValue(); - if(val === null || val === '' || val === undefined) return ''; - return parseInt(val); - } - }, + // {title:'검사수량', field:'INSPECTION_QTY', headerHozAlign:'center', hozAlign:'right', width:80, + // editor:"input", + // formatter: function(cell) { + // var val = cell.getValue(); + // if(val === null || val === '' || val === undefined) return ''; + // return parseInt(val); + // } + // }, {title:'불량수량', field:'DEFECT_QTY', headerHozAlign:'center', hozAlign:'right', width:80, - editor:"input", + editor:"number", + editorParams: {min:0, step:1}, formatter: function(cell) { var val = cell.getValue(); if(val === null || val === '' || val === undefined) return ''; return parseInt(val); } }, - {title:'불량율', field:'DEFECT_RATE', headerHozAlign:'center', hozAlign:'right', width:70, editor:false, - formatter: function(cell) { - var val = cell.getValue(); - if (val) return val + '%'; - return ''; - } - }, + // {title:'불량율', field:'DEFECT_RATE', headerHozAlign:'center', hozAlign:'right', width:70, editor:false, + // formatter: function(cell) { + // var val = cell.getValue(); + // if (val) return val + '%'; + // return ''; + // } + // }, {title:'검사일', field:'INSPECTION_DATE', headerHozAlign:'center', hozAlign:'center', width:100, editor:"date" }, @@ -427,25 +530,33 @@ function fn_initRightGrid() { rightGrid.on("cellEdited", function(cell) { var row = cell.getRow(); var data = row.getData(); + var field = cell.getField(); // 불량유형 변경 시 불량원인 초기화 - if (cell.getField() === 'DEFECT_TYPE') { + if (field === 'DEFECT_TYPE') { row.update({"DEFECT_REASON": ""}); } - // 검사수량, 불량수량 변경 시 불량율 자동 계산 - if (cell.getField() === 'INSPECTION_QTY' || cell.getField() === 'DEFECT_QTY') { + // 불량수량 변경 시 정수로 변환 + if (field === 'DEFECT_QTY') { + var defectQty = parseInt(data.DEFECT_QTY) || 0; + row.update({"DEFECT_QTY": defectQty}); + // 불량율 계산 + fn_calcDefectRate(row); + // 검사결과 자동 설정 + fn_updateInspectionResult(row); + // 좌측 그리드 불량수량 합계/불량율 업데이트 + fn_updateLeftGridDefectSum(); + } + + // 검사수량 변경 시 불량율 자동 계산 + if (field === 'INSPECTION_QTY') { fn_calcDefectRate(row); } - // 불량수량 변경 시 검사결과 자동 설정 - if (cell.getField() === 'DEFECT_QTY') { - var defectQty = parseInt(data.DEFECT_QTY) || 0; - if (defectQty > 0) { - row.update({"INSPECTION_RESULT": "NG"}); - } else { - row.update({"INSPECTION_RESULT": "OK"}); - } + // 처리결과 변경 시 검사결과 자동 설정 + if (field === 'ACTION_RESULT') { + fn_updateInspectionResult(row); } data.GRID_STATUS = data.GRID_STATUS === 'I' ? 'I' : 'U'; @@ -466,6 +577,28 @@ function fn_calcDefectRate(row) { } } +// 검사결과 자동 설정 +// 처리결과가 '수정' 또는 '특채'이면 OK, 불량수량 > 0 이면 NG, 그 외 OK +function fn_updateInspectionResult(row) { + var data = row.getData(); + var defectQty = parseInt(data.DEFECT_QTY) || 0; + var actionResult = fnc_checkNull(data.ACTION_RESULT); + + console.log("[검사결과 자동설정] 불량수량:", defectQty, ", 처리결과:", actionResult); + + var newResult = "OK"; + // 처리결과가 '수정' 또는 '특채'이면 불량수량 있어도 OK + if (actionResult === '수정' || actionResult === '특채') { + newResult = "OK"; + } else if (defectQty > 0) { + // 불량수량 있으면 NG + newResult = "NG"; + } + + console.log("[검사결과 자동설정] 결과:", newResult); + row.update({"INSPECTION_RESULT": newResult}); +} + // ===================================================== // 좌측 그리드 조회 // ===================================================== @@ -609,6 +742,8 @@ function fn_deleteDefectRow() { row.getElement().style.display = 'none'; } }); + // 좌측 그리드 불량수량 합계/불량율 업데이트 + fn_updateLeftGridDefectSum(); } }); } diff --git a/src/com/pms/mapper/quality.xml b/src/com/pms/mapper/quality.xml index a448a55..ba5892e 100644 --- a/src/com/pms/mapper/quality.xml +++ b/src/com/pms/mapper/quality.xml @@ -711,10 +711,9 @@ ,DEFECT.INSPECTOR_NAME_DISPLAY AS INSPECTOR_NAME /* 검사일: YYYY-MM-DD 외 N건 형태 */ ,DEFECT.INSPECTION_DATE_DISPLAY AS INSPECTION_DATE - /* 검사결과: 하나라도 NG면 NG, 검사 대상(스킵 제외) 모두 검사완료면 OK, 일부만 검사면 검사중 */ - ,(CASE WHEN DEFECT.NG_COUNT > 0 THEN 'NG' - WHEN DEFECT.INSPECTION_TARGET_COUNT > 0 AND DEFECT.INSPECTION_TARGET_COUNT = DEFECT.INSPECTED_COUNT THEN 'OK' - WHEN DEFECT.INSPECTED_COUNT > 0 THEN '검사중' + /* 검사현황: 불량상세 처리결과(ACTION_RESULT)가 전부 있으면 완료, 일부만 있으면 진행중 */ + ,(CASE WHEN DEFECT.DEFECT_TOTAL_COUNT > 0 AND DEFECT.DEFECT_TOTAL_COUNT = DEFECT.ACTION_RESULT_COUNT THEN '완료' + WHEN DEFECT.ACTION_RESULT_COUNT > 0 THEN '진행중' ELSE '' END) AS INSPECTION_RESULT /* 검사여부: 검사가 하나라도 있으면 '검사', 모두 스킵이면 '스킵', 아무것도 없으면 빈값 */ @@ -764,6 +763,10 @@ END AS INSPECTION_DATE_DISPLAY /* 검사 대상 건수 (스킵 제외, 검사인 항목만) */ ,COUNT(CASE WHEN IID2.INSPECTION_YN = '검사' THEN 1 END) AS INSPECTION_TARGET_COUNT + /* 불량상세 테이블 전체 건수 (검사인 항목 기준) */ + ,COUNT(CASE WHEN IID2.INSPECTION_YN = '검사' THEN IDF.OBJID END) AS DEFECT_TOTAL_COUNT + /* 처리결과(ACTION_RESULT) 입력된 건수 */ + ,COUNT(CASE WHEN IID2.INSPECTION_YN = '검사' AND IDF.ACTION_RESULT IS NOT NULL AND IDF.ACTION_RESULT != '' THEN 1 END) AS ACTION_RESULT_COUNT /* 검사결과 입력된 건수 */ ,COUNT(CASE WHEN IDF.INSPECTION_RESULT IS NOT NULL AND IDF.INSPECTION_RESULT != '' THEN 1 END) AS INSPECTED_COUNT ,COUNT(CASE WHEN IDF.INSPECTION_RESULT = 'NG' THEN 1 END) AS NG_COUNT @@ -2266,6 +2269,14 @@ , IID.REMARK , IMI.PURCHASE_ORDER_MASTER_OBJID , POM.PURCHASE_ORDER_NO + /* 불량수량 합계 (INCOMING_INSPECTION_DETAIL.DEFECT_QTY에 저장된 값 사용) */ + , COALESCE(NULLIF(IID.DEFECT_QTY, '')::NUMERIC, 0) AS DEFECT_QTY_SUM + /* 불량율 (불량수량합계 / 입고수량 * 100) */ + , CASE WHEN IMI.RECEIPT_QTY::NUMERIC > 0 + THEN ROUND(COALESCE(NULLIF(IID.DEFECT_QTY, '')::NUMERIC, 0) / IMI.RECEIPT_QTY::NUMERIC * 100, 2) + ELSE NULL END AS LEFT_DEFECT_RATE + /* 검사성적서 파일 수 */ + , (SELECT COUNT(*) FROM ATTACH_FILE_INFO AFI WHERE AFI.TARGET_OBJID = IID.OBJID AND AFI.DOC_TYPE = 'INSPECTION_REPORT' AND UPPER(AFI.STATUS) = 'ACTIVE') AS INSPECTION_FILE_CNT FROM INVENTORY_MGMT_IN IMI INNER JOIN INVENTORY_MGMT IM ON IM.OBJID = IMI.PARENT_OBJID INNER JOIN PART_MNG PM ON PM.OBJID::VARCHAR = IM.PART_OBJID diff --git a/src/com/pms/service/QualityService.java b/src/com/pms/service/QualityService.java index e600ebd..2f5d73c 100644 --- a/src/com/pms/service/QualityService.java +++ b/src/com/pms/service/QualityService.java @@ -1773,6 +1773,8 @@ public class QualityService extends BaseService{ sqlParamMap.put("INSPECTION_TYPE", CommonUtils.checkNull(data.get("INSPECTION_TYPE"))); sqlParamMap.put("INSPECTION_DATE", inspectionDate); sqlParamMap.put("INSPECTOR_ID", inspectorId); + sqlParamMap.put("INSPECTION_QTY", CommonUtils.checkNull(data.get("INSPECTION_QTY"))); // 검사수량 + sqlParamMap.put("DEFECT_QTY", CommonUtils.checkNull(data.get("DEFECT_QTY_SUM"))); // 불량수량 합계 sqlParamMap.put("WRITER", writer); sqlSession.update("quality.saveIncomingInspectionDetail", sqlParamMap); @@ -1788,7 +1790,7 @@ public class QualityService extends BaseService{ for(Map defect : defectList) { String gridStatus = CommonUtils.checkNull(defect.get("GRID_STATUS")); - + Map sqlParamMap = new HashMap(); sqlParamMap.put("OBJID", CommonUtils.checkNull(defect.get("OBJID"))); sqlParamMap.put("INSPECTION_DETAIL_OBJID", selectedDetailObjid); @@ -1804,7 +1806,7 @@ public class QualityService extends BaseService{ sqlParamMap.put("INSPECTION_RESULT", CommonUtils.checkNull(defect.get("INSPECTION_RESULT"))); sqlParamMap.put("REMARK", CommonUtils.checkNull(defect.get("REMARK"))); sqlParamMap.put("WRITER", writer); - + if("D".equals(gridStatus)) { // 삭제 sqlSession.delete("quality.deleteIncomingInspectionDefect", sqlParamMap);