From fa7797fdd9e97a59dee9cf91ba478d6dfb83653b Mon Sep 17 00:00:00 2001 From: leeheejin Date: Tue, 16 Dec 2025 17:59:55 +0900 Subject: [PATCH] =?UTF-8?q?=ED=8C=8C=EC=9D=BC=EC=A0=80=EC=9E=A5=EB=8F=84?= =?UTF-8?q?=20=ED=99=95=EC=9D=B8=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../classes/com/pms/mapper/quality.xml | 314 +++++++++- .../semiProductInspectionFormPopUp.jsp | 583 ++++++++++++------ src/com/pms/mapper/quality.xml | 45 ++ src/com/pms/service/QualityService.java | 113 +++- 4 files changed, 828 insertions(+), 227 deletions(-) diff --git a/WebContent/WEB-INF/classes/com/pms/mapper/quality.xml b/WebContent/WEB-INF/classes/com/pms/mapper/quality.xml index 4d6357e..5179bf6 100644 --- a/WebContent/WEB-INF/classes/com/pms/mapper/quality.xml +++ b/WebContent/WEB-INF/classes/com/pms/mapper/quality.xml @@ -666,7 +666,7 @@ ,POM.PURCHASE_ORDER_NO ,CM.PROJECT_NO - + ,CODE_NAME(CM.PRODUCT) AS PRODUCT_NAME ,(SELECT CASE @@ -702,24 +702,85 @@ ELSE '입고중' END) AS DELIVERY_STATUS - - ,(SELECT U.USER_NAME FROM USER_INFO U WHERE U.USER_ID = IID.INSPECTOR_ID) AS INSPECTOR_NAME - ,IID.INSPECTION_DATE - ,(CASE WHEN IID.NG_COUNT > 0 THEN 'NG' - WHEN IID.TOTAL_COUNT > 0 AND IID.TOTAL_COUNT = IID.INSPECTED_COUNT THEN 'OK' - WHEN IID.INSPECTED_COUNT > 0 THEN '검사중' + + ,(SELECT U.USER_NAME FROM USER_INFO U WHERE U.USER_ID = IID.REQUEST_USER_ID) AS REQUEST_USER_NAME + ,IID.REQUEST_DATE + + + /* 검사자: XXX 외 N건 형태 */ + ,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 '검사중' ELSE '' END) AS INSPECTION_RESULT + + /* 검사여부: 검사가 하나라도 있으면 '검사', 모두 스킵이면 '스킵', 아무것도 없으면 빈값 */ + ,(CASE WHEN IID.INSPECTION_YN_COUNT > 0 THEN '검사' + WHEN IID.SKIP_YN_COUNT > 0 AND IID.INSPECTION_YN_COUNT = 0 THEN '스킵' + ELSE '' END) AS INSPECTION_YN + + /* 요청현황: 입고내역 기준으로 검사여부 선택 현황 계산 + 미요청: 입고건이 있지만 전부 검사여부 미선택 + 요청중: 일부만 검사여부 선택됨 + 요청완료: 전부 검사여부 선택됨 */ + ,(CASE WHEN REQ.DELIVERY_TOTAL_COUNT IS NULL OR REQ.DELIVERY_TOTAL_COUNT = 0 THEN '' + WHEN REQ.SELECTED_COUNT = 0 THEN '미요청' + WHEN REQ.SELECTED_COUNT REQ.DELIVERY_TOTAL_COUNT THEN '요청중' + ELSE '요청완료' END) AS REQUEST_STATUS + ,(SELECT COUNT(1) FROM ATTACH_FILE_INFO AF WHERE AF.TARGET_OBJID = POM.OBJID AND AF.DOC_TYPE = 'INSPECTION_FILE' AND UPPER(AF.STATUS) = 'ACTIVE') AS INSPECTION_FILE_CNT FROM PURCHASE_ORDER_MASTER AS POM LEFT OUTER JOIN ( SELECT PURCHASE_ORDER_MASTER_OBJID - ,MAX(INSPECTOR_ID) AS INSPECTOR_ID - ,MAX(INSPECTION_DATE) AS INSPECTION_DATE + ,MAX(REQUEST_USER_ID) AS REQUEST_USER_ID + ,MAX(REQUEST_DATE) AS REQUEST_DATE ,COUNT(*) AS TOTAL_COUNT - ,COUNT(CASE WHEN INSPECTION_RESULT IS NOT NULL AND INSPECTION_RESULT != '' THEN 1 END) AS INSPECTED_COUNT - ,COUNT(CASE WHEN INSPECTION_RESULT = 'NG' THEN 1 END) AS NG_COUNT + /* 검사여부 카운트: 검사 선택 건수, 스킵 선택 건수 */ + ,COUNT(CASE WHEN INSPECTION_YN = '검사' THEN 1 END) AS INSPECTION_YN_COUNT + ,COUNT(CASE WHEN INSPECTION_YN = '스킵' THEN 1 END) AS SKIP_YN_COUNT FROM INCOMING_INSPECTION_DETAIL GROUP BY PURCHASE_ORDER_MASTER_OBJID ) AS IID ON POM.OBJID::VARCHAR = IID.PURCHASE_ORDER_MASTER_OBJID + /* 불량상세 테이블에서 검사일/검사자/검사결과 집계 (스킵 항목 제외) */ + LEFT OUTER JOIN ( + SELECT IID2.PURCHASE_ORDER_MASTER_OBJID + /* 검사자: 여러명이면 "XXX 외 N건" 형태로 표시 */ + ,CASE + WHEN COUNT(DISTINCT IDF.INSPECTOR_ID) > 1 + THEN (SELECT USER_NAME FROM USER_INFO WHERE USER_ID = MIN(IDF.INSPECTOR_ID)) || ' 외 ' || (COUNT(DISTINCT IDF.INSPECTOR_ID) - 1) || '건' + WHEN COUNT(DISTINCT IDF.INSPECTOR_ID) = 1 + THEN (SELECT USER_NAME FROM USER_INFO WHERE USER_ID = MIN(IDF.INSPECTOR_ID)) + ELSE '' + END AS INSPECTOR_NAME_DISPLAY + /* 검사일: 여러개면 "YYYY-MM-DD 외 N건" 형태로 표시 */ + ,CASE + WHEN COUNT(DISTINCT IDF.INSPECTION_DATE) > 1 + THEN TO_CHAR(MIN(IDF.INSPECTION_DATE), 'YYYY-MM-DD') || ' 외 ' || (COUNT(DISTINCT IDF.INSPECTION_DATE) - 1) || '건' + WHEN COUNT(DISTINCT IDF.INSPECTION_DATE) = 1 + THEN TO_CHAR(MIN(IDF.INSPECTION_DATE), 'YYYY-MM-DD') + ELSE '' + END AS INSPECTION_DATE_DISPLAY + /* 검사 대상 건수 (스킵 제외, 검사인 항목만) */ + ,COUNT(CASE WHEN IID2.INSPECTION_YN = '검사' THEN 1 END) AS INSPECTION_TARGET_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 + FROM INCOMING_INSPECTION_DETAIL IID2 + LEFT JOIN INCOMING_INSPECTION_DEFECT IDF ON IDF.INSPECTION_DETAIL_OBJID = IID2.OBJID + GROUP BY IID2.PURCHASE_ORDER_MASTER_OBJID + ) AS DEFECT ON POM.OBJID::VARCHAR = DEFECT.PURCHASE_ORDER_MASTER_OBJID + /* 입고내역 기준 검사여부 선택 현황 (요청현황 계산용) */ + LEFT OUTER JOIN ( + SELECT IMI.PURCHASE_ORDER_MASTER_OBJID + ,COUNT(*) AS DELIVERY_TOTAL_COUNT + ,COUNT(CASE WHEN IID.INSPECTION_YN IS NOT NULL AND IID.INSPECTION_YN != '' THEN 1 END) AS SELECTED_COUNT + FROM INVENTORY_MGMT_IN IMI + LEFT JOIN INCOMING_INSPECTION_DETAIL IID ON IID.INVENTORY_IN_OBJID = IMI.OBJID + WHERE IMI.PURCHASE_ORDER_MASTER_OBJID IS NOT NULL + GROUP BY IMI.PURCHASE_ORDER_MASTER_OBJID + ) AS REQ ON POM.OBJID::VARCHAR = REQ.PURCHASE_ORDER_MASTER_OBJID LEFT OUTER JOIN ( SELECT POP.PURCHASE_ORDER_MASTER_OBJID ,SUM(POP.ORDER_QTY::NUMERIC) AS TOTAL_PO_QTY @@ -792,6 +853,11 @@ WHEN IID.INSPECTED_COUNT > 0 THEN '검사중' ELSE '' END) = #{search_inspection_result} + + + AND REQ.DELIVERY_TOTAL_COUNT = REQ.SELECTED_COUNT + AND REQ.SELECTED_COUNT > 0 + ORDER BY POM.REGDATE DESC @@ -1497,6 +1563,7 @@ + + + @@ -2062,6 +2224,8 @@ OBJID , INVENTORY_IN_OBJID , PURCHASE_ORDER_MASTER_OBJID + , REQUEST_DATE + , REQUEST_USER_ID , INSPECTION_DATE , INSPECTOR_ID , INSPECTION_TYPE @@ -2080,6 +2244,8 @@ #{NEW_OBJID} , #{OBJID} , #{PURCHASE_ORDER_MASTER_OBJID} + , #{REQUEST_DATE} + , #{REQUEST_USER_ID} , #{INSPECTION_DATE} , #{INSPECTOR_ID} , #{INSPECTION_TYPE} @@ -2096,20 +2262,120 @@ , NOW() ) ON CONFLICT (INVENTORY_IN_OBJID) DO UPDATE SET - INSPECTION_DATE = #{INSPECTION_DATE} - , INSPECTOR_ID = #{INSPECTOR_ID} - , INSPECTION_TYPE = #{INSPECTION_TYPE} - , INSPECTION_YN = #{INSPECTION_YN} - , DEFECT_TYPE = #{DEFECT_TYPE} - , DEFECT_REASON = #{DEFECT_REASON} - , ACTION_STATUS = #{ACTION_STATUS} - , INSPECTION_QTY = #{INSPECTION_QTY} - , DEFECT_QTY = #{DEFECT_QTY} - , INSPECTION_RESULT = #{INSPECTION_RESULT} - , ATTACH_FILE_OBJID = #{ATTACH_FILE_OBJID} - , REMARK = #{REMARK} + REQUEST_DATE = COALESCE(#{REQUEST_DATE}, INCOMING_INSPECTION_DETAIL.REQUEST_DATE) + , REQUEST_USER_ID = COALESCE(#{REQUEST_USER_ID}, INCOMING_INSPECTION_DETAIL.REQUEST_USER_ID) + , INSPECTION_DATE = COALESCE(#{INSPECTION_DATE}, INCOMING_INSPECTION_DETAIL.INSPECTION_DATE) + , INSPECTOR_ID = COALESCE(#{INSPECTOR_ID}, INCOMING_INSPECTION_DETAIL.INSPECTOR_ID) + , INSPECTION_TYPE = COALESCE(#{INSPECTION_TYPE}, INCOMING_INSPECTION_DETAIL.INSPECTION_TYPE) + , INSPECTION_YN = COALESCE(#{INSPECTION_YN}, INCOMING_INSPECTION_DETAIL.INSPECTION_YN) + , DEFECT_TYPE = COALESCE(#{DEFECT_TYPE}, INCOMING_INSPECTION_DETAIL.DEFECT_TYPE) + , DEFECT_REASON = COALESCE(#{DEFECT_REASON}, INCOMING_INSPECTION_DETAIL.DEFECT_REASON) + , ACTION_STATUS = COALESCE(#{ACTION_STATUS}, INCOMING_INSPECTION_DETAIL.ACTION_STATUS) + , INSPECTION_QTY = COALESCE(#{INSPECTION_QTY}, INCOMING_INSPECTION_DETAIL.INSPECTION_QTY) + , DEFECT_QTY = COALESCE(#{DEFECT_QTY}, INCOMING_INSPECTION_DETAIL.DEFECT_QTY) + , INSPECTION_RESULT = COALESCE(#{INSPECTION_RESULT}, INCOMING_INSPECTION_DETAIL.INSPECTION_RESULT) + , ATTACH_FILE_OBJID = COALESCE(#{ATTACH_FILE_OBJID}, INCOMING_INSPECTION_DETAIL.ATTACH_FILE_OBJID) + , REMARK = COALESCE(#{REMARK}, INCOMING_INSPECTION_DETAIL.REMARK) , MODIFIER = #{WRITER} , MOD_DATE = NOW() + + + + + + + + INSERT INTO INCOMING_INSPECTION_DEFECT ( + OBJID + , INSPECTION_DETAIL_OBJID + , INSPECTION_TYPE + , INSPECTION_DATE + , INSPECTOR_ID + , DEFECT_TYPE + , DEFECT_REASON + , ACTION_STATUS + , ACTION_RESULT + , INSPECTION_QTY + , DEFECT_QTY + , INSPECTION_RESULT + , REMARK + , WRITER + , REG_DATE + ) VALUES ( + #{OBJID} + , #{INSPECTION_DETAIL_OBJID} + , #{INSPECTION_TYPE} + , #{INSPECTION_DATE}::DATE + , #{INSPECTOR_ID} + , #{DEFECT_TYPE} + , #{DEFECT_REASON} + , #{ACTION_STATUS} + , #{ACTION_RESULT} + , #{INSPECTION_QTY} + , #{DEFECT_QTY} + , #{INSPECTION_RESULT} + , #{REMARK} + , #{WRITER} + , NOW() + ) + ON CONFLICT (OBJID) DO UPDATE SET + INSPECTION_TYPE = #{INSPECTION_TYPE} + , INSPECTION_DATE = #{INSPECTION_DATE}::DATE + , INSPECTOR_ID = #{INSPECTOR_ID} + , DEFECT_TYPE = #{DEFECT_TYPE} + , DEFECT_REASON = #{DEFECT_REASON} + , ACTION_STATUS = #{ACTION_STATUS} + , ACTION_RESULT = #{ACTION_RESULT} + , INSPECTION_QTY = #{INSPECTION_QTY} + , DEFECT_QTY = #{DEFECT_QTY} + , INSPECTION_RESULT = #{INSPECTION_RESULT} + , REMARK = #{REMARK} + , MODIFIER = #{WRITER} + , MOD_DATE = NOW() + + + + + DELETE FROM INCOMING_INSPECTION_DEFECT + WHERE OBJID = #{OBJID} + + + + + DELETE FROM INCOMING_INSPECTION_DEFECT + WHERE INSPECTION_DETAIL_OBJID = #{INSPECTION_DETAIL_OBJID} + + \ No newline at end of file diff --git a/WebContent/WEB-INF/view/quality/semiProductInspectionFormPopUp.jsp b/WebContent/WEB-INF/view/quality/semiProductInspectionFormPopUp.jsp index 4ea2d53..924e006 100644 --- a/WebContent/WEB-INF/view/quality/semiProductInspectionFormPopUp.jsp +++ b/WebContent/WEB-INF/view/quality/semiProductInspectionFormPopUp.jsp @@ -18,6 +18,8 @@ PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); String loginUserName = CommonUtils.checkNull(person.getUserName()); String loginUserId = CommonUtils.checkNull(person.getUserId()); %> + + @@ -48,7 +50,8 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); justify-content: space-between; align-items: center; padding: 10px 15px; - background: #f5f5f5; + background: #4a5568; + color: white; border-bottom: 1px solid #ddd; } @@ -62,57 +65,59 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); display: flex; flex: 1; overflow: hidden; + gap: 10px; + padding: 10px; } - /* 왼쪽 영역 */ - .left_section { - width: 50%; + /* 좌측 패널 (35%) */ + .left_panel { + width: 35%; display: flex; flex-direction: column; - border-right: 2px solid #ccc; - padding: 10px; - box-sizing: border-box; + border: 1px solid #ddd; + border-radius: 4px; + overflow: hidden; } - /* 오른쪽 영역 */ - .right_section { - width: 50%; + /* 우측 패널 (65%) */ + .right_panel { + width: 65%; display: flex; flex-direction: column; - padding: 10px; - box-sizing: border-box; + border: 1px solid #ddd; + border-radius: 4px; + overflow: hidden; } - .section_header { + .panel_header { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 8px; - padding-bottom: 5px; - border-bottom: 1px solid #e0e0e0; - } - - .section_header h3 { - margin: 0; - font-size: 14px; + padding: 8px 12px; + background: #4a5568; + color: white; font-weight: bold; - color: #333; + font-size: 13px; } - .section_header .btn_area { + .panel_header .btn_group { display: flex; gap: 5px; } - .grid_wrap { + .panel_body { flex: 1; - overflow: hidden; + overflow: auto; + } + + .grid_wrap { + height: 100%; } /* 버튼 스타일 */ .btn_add { - padding: 4px 12px; - background: #4CAF50; + padding: 4px 10px; + background: #48bb78; color: white; border: none; border-radius: 3px; @@ -121,12 +126,12 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); } .btn_add:hover { - background: #45a049; + background: #38a169; } .btn_del { - padding: 4px 12px; - background: #f44336; + padding: 4px 10px; + background: #e53e3e; color: white; border: none; border-radius: 3px; @@ -135,12 +140,12 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); } .btn_del:hover { - background: #da190b; + background: #c53030; } .btn_save { padding: 6px 20px; - background: #2196F3; + background: #3182ce; color: white; border: none; border-radius: 3px; @@ -150,12 +155,12 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); } .btn_save:hover { - background: #1976D2; + background: #2b6cb0; } .btn_close { padding: 6px 20px; - background: #757575; + background: #718096; color: white; border: none; border-radius: 3px; @@ -165,7 +170,7 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); } .btn_close:hover { - background: #616161; + background: #4a5568; } /* 헤더 버튼 영역 */ @@ -174,9 +179,20 @@ String loginUserId = CommonUtils.checkNull(person.getUserId()); gap: 8px; } - /* Tabulator 편집 가능 셀 스타일 */ - .tabulator .tabulator-cell.editable-cell { - background-color: #fffde7; + /* 선택된 행 하이라이트 */ + .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 스타일 */ @@ -202,54 +218,69 @@ String loginUserId = CommonUtils.checkNull(person.getUserId());