파일저장도 확인완료

This commit is contained in:
leeheejin
2025-12-16 17:59:55 +09:00
parent a8fbd65757
commit fa7797fdd9
4 changed files with 828 additions and 227 deletions

View File

@@ -666,7 +666,7 @@
,POM.PURCHASE_ORDER_NO
,CM.PROJECT_NO
,CODE_NAME(CM.PRODUCT) AS PRODUCT_NAME
<!-- 품번/품명 (첫번째 + 외 N건) -->
,(SELECT
CASE
@@ -702,24 +702,85 @@
ELSE '입고중'
END) AS DELIVERY_STATUS
<!-- 검사 정보 (INCOMING_INSPECTION_DETAIL에서 조회) -->
,(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 '검사중'
<!-- 요청 정보 (INCOMING_INSPECTION_DETAIL에서 조회) -->
,(SELECT U.USER_NAME FROM USER_INFO U WHERE U.USER_ID = IID.REQUEST_USER_ID) AS REQUEST_USER_NAME
,IID.REQUEST_DATE
<!-- 검사 정보 (INCOMING_INSPECTION_DEFECT에서 조회) -->
/* 검사자: 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 <![CDATA[ < ]]> 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}
</if>
<!-- 요청현황 필터 (수입검사 진행 리스트에서 요청완료 건만 조회) -->
<if test="request_status_filter != null and request_status_filter == '요청완료'">
AND REQ.DELIVERY_TOTAL_COUNT = REQ.SELECTED_COUNT
AND REQ.SELECTED_COUNT > 0
</if>
ORDER BY POM.REGDATE DESC
</select>
@@ -1497,6 +1563,7 @@
<!-- 반제품검사 상세 조회 (타입별) -->
<select id="getSemiProductInspectionByType" parameterType="map" resultType="map">
SELECT SPI.OBJID AS "OBJID"
, SPI.OBJID AS "ROW_ID"
, COALESCE(SPI.MODEL_NAME, '') AS "MODEL_NAME"
, COALESCE(SPI.PRODUCT_TYPE, '') AS "PRODUCT_TYPE"
, COALESCE(SPI.WORK_ORDER_NO, '') AS "WORK_ORDER_NO"
@@ -1515,6 +1582,10 @@
, COALESCE(SPI.REMARK, '') AS "REMARK"
, SPI.DATA_TYPE AS "DATA_TYPE"
, COALESCE(SPI.INSPECTION_GROUP_ID, '') AS "INSPECTION_GROUP_ID"
<!-- 파일 카운트 조회 -->
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO AFI WHERE AFI.TARGET_OBJID = SPI.OBJID AND AFI.DOC_TYPE = 'SEMI_INSPECTION_IMAGE' AND AFI.STATUS = 'Active') AS "IMAGE_FILE_CNT"
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO AFI WHERE AFI.TARGET_OBJID = SPI.OBJID AND AFI.DOC_TYPE = 'SEMI_INSPECTION_NCR' AND AFI.STATUS = 'Active') AS "NCR_FILE_CNT"
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO AFI WHERE AFI.TARGET_OBJID = SPI.OBJID AND AFI.DOC_TYPE = 'SEMI_INSPECTION_REPORT' AND AFI.STATUS = 'Active') AS "REPORT_FILE_CNT"
FROM PMS_QUALITY_SEMI_PRODUCT_INSPECTION SPI
WHERE 1=1
<if test="INSPECTION_GROUP_ID != null and INSPECTION_GROUP_ID != ''">
@@ -1574,6 +1645,12 @@
WHERE OBJID = #{OBJID}
</update>
<!-- 반제품검사 데이터 삭제 (그룹ID 기준) -->
<delete id="deleteSemiProductInspectionByGroupId" parameterType="map">
DELETE FROM PMS_QUALITY_SEMI_PRODUCT_INSPECTION
WHERE INSPECTION_GROUP_ID = #{INSPECTION_GROUP_ID}
</delete>
<!-- 반제품검사 데이터 저장 (새 팝업 형식) -->
<insert id="insertSemiProductInspectionData" parameterType="map">
INSERT INTO PMS_QUALITY_SEMI_PRODUCT_INSPECTION (
@@ -1623,6 +1700,40 @@
)
</insert>
<!-- 반제품검사 데이터 수정 (기존 OBJID 유지) -->
<update id="updateSemiProductInspectionData" parameterType="map">
UPDATE PMS_QUALITY_SEMI_PRODUCT_INSPECTION SET
MODEL_NAME = #{MODEL_NAME}
, PRODUCT_TYPE = #{PRODUCT_TYPE}
, WORK_ORDER_NO = #{WORK_ORDER_NO}
, PART_NO = #{PART_NO}
, PART_NAME = #{PART_NAME}
, RECEIPT_QTY = COALESCE(NULLIF(#{RECEIPT_QTY}, '')::NUMERIC, 0)
, GOOD_QTY = COALESCE(NULLIF(#{GOOD_QTY}, '')::NUMERIC, 0)
, DEFECT_QTY = COALESCE(NULLIF(#{DEFECT_QTY}, '')::NUMERIC, 0)
, DEFECT_TYPE = #{DEFECT_TYPE}
, DEFECT_CAUSE = #{DEFECT_CAUSE}
, RESPONSIBLE_DEPT = #{RESPONSIBLE_DEPT}
, PROCESS_STATUS = #{PROCESS_STATUS}
, INSPECTION_DATE = CASE WHEN #{INSPECTION_DATE} = '' THEN NULL ELSE #{INSPECTION_DATE}::DATE END
, INSPECTOR = #{INSPECTOR}
, DISPOSITION_TYPE = #{DISPOSITION_TYPE}
, REMARK = #{REMARK}
WHERE OBJID = #{OBJID}
</update>
<!-- 반제품검사 데이터 삭제 (특정 OBJID 제외) -->
<delete id="deleteSemiProductInspectionExcludeObjIds" parameterType="map">
DELETE FROM PMS_QUALITY_SEMI_PRODUCT_INSPECTION
WHERE INSPECTION_GROUP_ID = #{INSPECTION_GROUP_ID}
<if test="EXCLUDE_OBJIDS != null and EXCLUDE_OBJIDS.size() > 0">
AND OBJID NOT IN
<foreach collection="EXCLUDE_OBJIDS" item="objId" open="(" separator="," close=")">
#{objId}
</foreach>
</if>
</delete>
<!-- =====================================================
고객 CS 관리
===================================================== -->
@@ -2020,7 +2131,10 @@
) THEN 'Y' ELSE 'N' END AS INSPECTION_FILE
, IID.OBJID AS INSPECTION_DETAIL_OBJID
, IID.INSPECTION_TYPE
, COALESCE(IID.INSPECTION_YN, '검사') AS INSPECTION_YN
, COALESCE(IID.INSPECTION_YN, '') AS INSPECTION_YN
, IID.REQUEST_DATE
, IID.REQUEST_USER_ID
, (SELECT USER_NAME FROM USER_INFO WHERE USER_ID = IID.REQUEST_USER_ID) AS REQUEST_USER_NAME
, IID.DEFECT_TYPE
, (SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = IID.DEFECT_TYPE) AS DEFECT_TYPE_NAME
, IID.DEFECT_REASON
@@ -2043,6 +2157,54 @@
LEFT JOIN PURCHASE_ORDER_MASTER POM ON POM.OBJID = IMI.PURCHASE_ORDER_MASTER_OBJID
LEFT JOIN INCOMING_INSPECTION_DETAIL IID ON IID.INVENTORY_IN_OBJID = IMI.OBJID
WHERE IMI.PURCHASE_ORDER_MASTER_OBJID = #{PURCHASE_ORDER_MASTER_OBJID}
<!-- <if test="INSPECTION_YN != null and INSPECTION_YN != ''">
AND IID.INSPECTION_YN = #{INSPECTION_YN}
</if> -->
ORDER BY IMI.REGDATE, PM.PART_NO
</select>
<!-- 수입검사 진행 상세 그리드 목록 조회 (검사여부가 '검사'인 항목만) -->
<select id="getIncomingInspectionProgressDetailList" parameterType="map" resultType="map">
SELECT IMI.OBJID
, IMI.PARENT_OBJID
, IM.PART_OBJID
, PM.PART_NO
, PM.PART_NAME
, COALESCE(IMI.RECEIPT_DATE, TO_CHAR(IMI.REGDATE, 'YYYY-MM-DD')) AS DELIVERY_DATE
, IMI.RECEIPT_QTY AS DELIVERY_QTY
, CASE WHEN EXISTS (
SELECT 1 FROM ATTACH_FILE_INFO DF
WHERE DF.TARGET_OBJID = POM.OBJID::VARCHAR
AND DF.DOC_TYPE = 'INSPECTION_FILE'
) THEN 'Y' ELSE 'N' END AS INSPECTION_FILE
, IID.OBJID AS INSPECTION_DETAIL_OBJID
, IID.INSPECTION_TYPE
, IID.INSPECTION_YN
, IID.REQUEST_DATE
, IID.REQUEST_USER_ID
, (SELECT USER_NAME FROM USER_INFO WHERE USER_ID = IID.REQUEST_USER_ID) AS REQUEST_USER_NAME
, IID.DEFECT_TYPE
, (SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = IID.DEFECT_TYPE) AS DEFECT_TYPE_NAME
, IID.DEFECT_REASON
, (SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = IID.DEFECT_REASON) AS DEFECT_REASON_NAME
, IID.ACTION_STATUS
, IID.INSPECTION_QTY
, IID.DEFECT_QTY
, CASE WHEN NULLIF(IID.INSPECTION_QTY, '')::NUMERIC > 0
THEN ROUND((COALESCE(NULLIF(IID.DEFECT_QTY, '')::NUMERIC, 0) / NULLIF(IID.INSPECTION_QTY, '')::NUMERIC) * 100, 2)
ELSE NULL END AS DEFECT_RATE
, IID.INSPECTION_RESULT
, IID.ATTACH_FILE_OBJID
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO AFI WHERE AFI.TARGET_OBJID = IMI.OBJID AND AFI.DOC_TYPE = 'INCOMING_INSPECTION' AND STATUS = 'Active') AS ATTACH_FILE_CNT
, IID.REMARK
, IMI.PURCHASE_ORDER_MASTER_OBJID
, POM.PURCHASE_ORDER_NO
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
LEFT JOIN PURCHASE_ORDER_MASTER POM ON POM.OBJID = IMI.PURCHASE_ORDER_MASTER_OBJID
INNER JOIN INCOMING_INSPECTION_DETAIL IID ON IID.INVENTORY_IN_OBJID = IMI.OBJID AND IID.INSPECTION_YN = '검사'
WHERE IMI.PURCHASE_ORDER_MASTER_OBJID = #{PURCHASE_ORDER_MASTER_OBJID}
ORDER BY IMI.REGDATE, PM.PART_NO
</select>
@@ -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()
</update>
<!-- =====================================================
수입검사 불량상세 (INCOMING_INSPECTION_DEFECT) 관련 쿼리
===================================================== -->
<!-- 불량상세 목록 조회 -->
<select id="getIncomingInspectionDefectList" parameterType="map" resultType="map">
SELECT
D.OBJID
, D.INSPECTION_DETAIL_OBJID
, D.INSPECTION_TYPE
, TO_CHAR(D.INSPECTION_DATE, 'YYYY-MM-DD') AS INSPECTION_DATE
, D.INSPECTOR_ID
, U.USER_NAME AS INSPECTOR_NAME
, D.DEFECT_TYPE
, D.DEFECT_REASON
, D.ACTION_STATUS
, D.ACTION_RESULT
, D.INSPECTION_QTY
, D.DEFECT_QTY
, CASE WHEN COALESCE(NULLIF(D.INSPECTION_QTY, ''), '0')::NUMERIC > 0
THEN ROUND((COALESCE(NULLIF(D.DEFECT_QTY, ''), '0')::NUMERIC / COALESCE(NULLIF(D.INSPECTION_QTY, ''), '1')::NUMERIC) * 100, 2)
ELSE 0
END AS DEFECT_RATE
, D.INSPECTION_RESULT
, D.REMARK
, D.WRITER
, TO_CHAR(D.REG_DATE, 'YYYY-MM-DD') AS REG_DATE
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO F WHERE F.TARGET_OBJID = D.OBJID AND F.DOC_TYPE = 'INSPECTION_DEFECT_IMAGE' AND UPPER(F.STATUS) = 'ACTIVE') AS IMAGE_FILE_CNT
, (SELECT COUNT(*) FROM ATTACH_FILE_INFO F WHERE F.TARGET_OBJID = D.OBJID AND F.DOC_TYPE = 'INSPECTION_DEFECT_FILE' AND UPPER(F.STATUS) = 'ACTIVE') AS ATTACH_FILE_CNT
FROM INCOMING_INSPECTION_DEFECT D
LEFT JOIN USER_INFO U ON D.INSPECTOR_ID = U.USER_ID
WHERE D.INSPECTION_DETAIL_OBJID = #{INSPECTION_DETAIL_OBJID}
ORDER BY D.REG_DATE ASC
</select>
<!-- 불량상세 저장 (INSERT/UPDATE) -->
<insert id="saveIncomingInspectionDefect" parameterType="map">
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()
</insert>
<!-- 불량상세 삭제 -->
<delete id="deleteIncomingInspectionDefect" parameterType="map">
DELETE FROM INCOMING_INSPECTION_DEFECT
WHERE OBJID = #{OBJID}
</delete>
<!-- 불량상세 전체 삭제 (검사상세 기준) -->
<delete id="deleteIncomingInspectionDefectByDetail" parameterType="map">
DELETE FROM INCOMING_INSPECTION_DEFECT
WHERE INSPECTION_DETAIL_OBJID = #{INSPECTION_DETAIL_OBJID}
</delete>
</mapper>

View File

@@ -18,6 +18,8 @@ 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>
@@ -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());
<!-- 컨텐츠 영역 -->
<div class="popup_content">
<!-- 왼쪽 영역: 양품 정보 (입고관리에서 가져옴) -->
<div class="left_section">
<div class="section_header">
<h3>양품 정보 (입고관리)</h3>
<div class="btn_area">
<button type="button" class="btn_add" onclick="fn_addLeftRow()">행추가</button>
<button type="button" class="btn_del" onclick="fn_delLeftRow()">행삭제</button>
<!-- 좌측: 양품 정보 (입고관리에서 가져옴) -->
<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>
</div>
</div>
<div class="grid_wrap" id="leftGridWrap"></div>
<div class="panel_body">
<div id="leftGrid" class="grid_wrap"></div>
</div>
</div>
<!-- 오른쪽 영역: 불량 정보 -->
<div class="right_section">
<div class="section_header">
<h3>불량 정보</h3>
<div class="btn_area">
<button type="button" class="btn_add" onclick="fn_addRightRow()">행추가</button>
<button type="button" class="btn_del" onclick="fn_delRightRow()">행삭제</button>
<!-- 우측: 불량 정보 -->
<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>
</div>
</div>
<div class="grid_wrap" id="rightGridWrap"></div>
<div class="panel_body" id="rightPanelBody">
<div id="rightGrid" class="grid_wrap"></div>
</div>
</div>
</div>
</div>
<script type="text/javascript">
var leftGrid; // 왼쪽 그리드 (양품 정보)
var rightGrid; // 오른쪽 그리드 (불량 정보)
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 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 = ["특채", "수정", "폐기"]; // 처리결과
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(){
// 입고관리 데이터 로드
@@ -266,15 +297,13 @@ $(document).ready(function(){
fn_loadData(objid, inspectionGroupId);
}
// 저장 버튼
$("#btnSave").click(function(){
fn_save();
});
// 닫기 버튼
$("#btnClose").click(function(){
window.close();
});
// 버튼 이벤트
$("#btnSave").click(fn_save);
$("#btnClose").click(function(){ window.close(); });
$("#btnAddLeft").click(fn_addLeftRow);
$("#btnDelLeft").click(fn_delLeftRow);
$("#btnAddRight").click(fn_addRightRow);
$("#btnDelRight").click(fn_delRightRow);
});
// 입고관리 데이터 로드
@@ -288,11 +317,7 @@ function fn_loadDeliveryData(){
if(result && result.list){
deliveryDataList = result.list;
// 중복 제거하여 각 목록 생성
var modelSet = {};
var partNoSet = {};
var partNameSet = {};
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;
@@ -320,20 +345,16 @@ function createSelect2Editor(options, allowClear) {
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;
}
if(opt == cellValue) option.selected = true;
select.appendChild(option);
});
@@ -352,7 +373,6 @@ function createSelect2Editor(options, allowClear) {
success($(select).val() || '');
});
// Select2 열기
$(select).select2('open');
});
@@ -360,21 +380,22 @@ function createSelect2Editor(options, allowClear) {
};
}
// 왼쪽 그리드 초기화 (양품 정보 - 입고관리 연동)
// =====================================================
// 좌측 그리드 (양품 정보) 초기화
// =====================================================
function fn_initLeftGrid(){
var columns = [
{formatter:"rowSelection", titleFormatter:"rowSelection", hozAlign:"center", headerSort:false, width:30},
{title:"품명(모델명)", field:"MODEL_NAME", minWidth:130, headerSort:false,
{title:"품명(모델명)", field:"MODEL_NAME", minWidth:120, headerSort:false,
editor: createSelect2Editor(modelNameList)
},
{title:"제품구분", field:"PRODUCT_TYPE", minWidth:80, headerSort:false,
editor: createSelect2Editor(productTypeList)
},
{title:"작업지시번호", field:"WORK_ORDER_NO", editor:"input", minWidth:110, headerSort:false},
{title:"부품품번", field:"PART_NO", minWidth:110, headerSort:false,
{title:"작업지시번호", field:"WORK_ORDER_NO", editor:"input", minWidth:100, headerSort:false},
{title:"부품품번", field:"PART_NO", minWidth:100, headerSort:false,
editor: createSelect2Editor(partNoList),
cellEdited: function(cell){
// 부품품번 선택 시 관련 데이터 자동 입력
var partNo = cell.getValue();
if(partNo){
var matched = deliveryDataList.find(function(d){ return d.PART_NO == partNo; });
@@ -387,10 +408,9 @@ function fn_initLeftGrid(){
}
}
},
{title:"부품명", field:"PART_NAME", minWidth:130, headerSort:false,
{title:"부품명", field:"PART_NAME", minWidth:120, headerSort:false,
editor: createSelect2Editor(partNameList),
cellEdited: function(cell){
// 부품명 선택 시 관련 데이터 자동 입력
var partName = cell.getValue();
if(partName){
var matched = deliveryDataList.find(function(d){ return d.PART_NAME == partName; });
@@ -403,33 +423,58 @@ function fn_initLeftGrid(){
}
}
},
{title:"입고수량", field:"RECEIPT_QTY", editor:"number", hozAlign:"right", minWidth:80, headerSort:false,
{title:"입고수량", field:"RECEIPT_QTY", editor:"number", hozAlign:"right", minWidth:70, headerSort:false,
editorParams:{min:0, step:1},
formatter: function(cell){
var val = cell.getValue();
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "";
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
}
},
{title:"양품수량", field:"GOOD_QTY", editor:"number", hozAlign:"right", minWidth:80, headerSort:false,
{title:"양품수량", field:"GOOD_QTY", editor:"number", hozAlign:"right", minWidth:70, headerSort:false,
editorParams:{min:0, step:1},
formatter: function(cell){
var val = cell.getValue();
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "";
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
}
}
];
leftGrid = new Tabulator("#leftGridWrap", {
leftGrid = new Tabulator("#leftGrid", {
layout: "fitColumns",
height: "100%",
columns: columns,
data: [],
placeholder: "데이터가 없습니다. 행을 추가해주세요.",
selectable: true
placeholder: "행을 추가해주세요.",
selectable: 1 // 단일 선택
});
// 행 선택 이벤트 - 해당 행의 불량 정보를 우측에 표시
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},
@@ -437,110 +482,200 @@ function fn_initRightGrid(){
editorParams:{min:0, step:1},
formatter: function(cell){
var val = cell.getValue();
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "";
return (val !== null && val !== undefined && val !== "") ? Number(val).toLocaleString() : "0";
}
},
{title:"불량유형", field:"DEFECT_TYPE", minWidth:85, headerSort:false,
editor: createSelect2Editor(defectTypeList)
},
{title:"불량원인", field:"DEFECT_CAUSE", minWidth:100, headerSort:false,
{title:"불량원인", field:"DEFECT_CAUSE", minWidth:90, headerSort:false,
editor: createSelect2Editor(defectCauseList)
},
{title:"귀책부서", field:"RESPONSIBLE_DEPT", minWidth:80, headerSort:false,
editor: createSelect2Editor(responsibleDeptList)
},
{title:"부적합보고서", field:"NCR_FILE", minWidth:90, headerSort:false,
formatter: function(cell){
return '<button type="button" onclick="fn_uploadNCR(this)" style="padding:2px 8px;font-size:11px;">첨부</button>';
{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 && !objId.startsWith("DEFECT_")){
fn_openNCRFilePopUp(objId);
} else {
Swal.fire({icon:'info', title:'알림', text:'먼저 저장 후 파일을 등록할 수 있습니다.'});
}
}
},
{title:"처리현황", field:"PROCESS_STATUS", minWidth:80, headerSort:false,
{title:"처리현황", field:"PROCESS_STATUS", minWidth:75, headerSort:false,
editor: createSelect2Editor(processStatusList)
},
{title:"이미지파일", field:"IMAGE_FILE", minWidth:80, headerSort:false,
formatter: function(cell){
return '<button type="button" onclick="fn_uploadImage(this)" style="padding:2px 8px;font-size:11px;">첨부</button>';
{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 && !objId.startsWith("DEFECT_")){
fn_openImageFilePopUp(objId);
} else {
Swal.fire({icon:'info', title:'알림', text:'먼저 저장 후 이미지를 등록할 수 있습니다.'});
}
}
},
{title:"검사일", field:"INSPECTION_DATE", minWidth:100, headerSort:false,
editor: "input",
editorParams: {
elementAttributes: {
type: "date"
}
}
editorParams: { elementAttributes: { type: "date" } }
},
{title:"검사자", field:"INSPECTOR", editor:"input", minWidth:80, headerSort:false},
{title:"처리결과", field:"DISPOSITION_TYPE", minWidth:80, headerSort:false,
{title:"검사자", field:"INSPECTOR", editor:"input", minWidth:70, headerSort:false},
{title:"처리결과", field:"DISPOSITION_TYPE", minWidth:75, headerSort:false,
editor: createSelect2Editor(dispositionTypeList)
},
{title:"검사성적서", field:"INSPECTION_REPORT", minWidth:80, headerSort:false,
formatter: function(cell){
return '<button type="button" onclick="fn_uploadReport(this)" style="padding:2px 8px;font-size:11px;">첨부</button>';
{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 && !objId.startsWith("DEFECT_")){
fn_openReportFilePopUp(objId);
} else {
Swal.fire({icon:'info', title:'알림', text:'먼저 저장 후 파일을 등록할 수 있습니다.'});
}
}
},
{title:"비고", field:"REMARK", editor:"input", minWidth:100, headerSort:false}
];
rightGrid = new Tabulator("#rightGridWrap", {
rightGrid = new Tabulator("#rightGrid", {
layout: "fitColumns",
height: "100%",
columns: columns,
data: [],
placeholder: "데이터가 없습니다. 행을 추가해주세요.",
placeholder: "좌측에서 양품 정보를 선택하세요.",
selectable: true
});
}
// 왼쪽 그리드 행 추가
// 우측 그리드에 데이터 표시 (선택된 좌측 행의 불량 데이터)
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(){
rowSeq++;
var newRowId = "NEW_" + rowSeq;
leftGrid.addRow({
ROW_ID: "NEW_" + rowSeq,
ROW_ID: newRowId,
MODEL_NAME: "",
PRODUCT_TYPE: "",
WORK_ORDER_NO: "",
PART_NO: "",
PART_NAME: "",
RECEIPT_QTY: "",
GOOD_QTY: ""
RECEIPT_QTY: 0,
GOOD_QTY: 0
});
// 새 행에 대한 불량 데이터 배열 초기화
allDefectData[newRowId] = [];
}
// 왼쪽 그리드 행 삭제
// 좌측 그리드 행 삭제
function fn_delLeftRow(){
var selectedRows = leftGrid.getSelectedRows();
if(selectedRows.length == 0){
Swal.fire("삭제할 행을 선택해주세요.");
return;
}
selectedRows.forEach(function(row){
row.delete();
Swal.fire({
title: '삭제 확인',
text: '선택한 행과 연결된 불량 정보도 함께 삭제됩니다. 삭제하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: '삭제',
cancelButtonText: '취소'
}).then(function(result){
if(result.isConfirmed){
selectedRows.forEach(function(row){
var rowId = row.getData().ROW_ID;
// 해당 행의 불량 데이터도 삭제
delete allDefectData[rowId];
row.delete();
});
// 우측 그리드 초기화
selectedLeftRowId = null;
selectedLeftRowData = null;
fn_clearRightGrid();
}
});
}
// 오른쪽 그리드 행 추가
// =====================================================
// 우측 그리드 행 추가
// =====================================================
function fn_addRightRow(){
if(!selectedLeftRowId){
Swal.fire("좌측에서 양품 정보를 먼저 선택하세요.");
return;
}
rowSeq++;
var today = new Date().toISOString().split('T')[0];
rightGrid.addRow({
ROW_ID: "NEW_" + rowSeq,
DEFECT_QTY: "",
ROW_ID: "DEFECT_" + rowSeq,
PARENT_ROW_ID: selectedLeftRowId, // 부모(좌측) 행 ID 연결
DEFECT_QTY: 0,
DEFECT_TYPE: "",
DEFECT_CAUSE: "",
RESPONSIBLE_DEPT: "",
NCR_FILE: "",
NCR_FILE_CNT: 0,
PROCESS_STATUS: "",
IMAGE_FILE: "",
IMAGE_FILE_CNT: 0,
INSPECTION_DATE: today,
INSPECTOR: loginUserName, // 로그인 사용자 자동 입력
INSPECTOR: loginUserName,
DISPOSITION_TYPE: "",
INSPECTION_REPORT: "",
REPORT_FILE_CNT: 0,
REMARK: ""
});
}
// 오른쪽 그리드 행 삭제
// 우측 그리드 행 삭제
function fn_delRightRow(){
var selectedRows = rightGrid.getSelectedRows();
if(selectedRows.length == 0){
@@ -552,34 +687,91 @@ function fn_delRightRow(){
});
}
// 이미지 파일 업로드
function fn_uploadImage(btn){
Swal.fire({
icon: 'info',
title: '이미지 첨부',
text: '이미지 파일 업로드 기능은 추후 구현 예정입니다.'
// =====================================================
// 파일 첨부 팝업
// =====================================================
// 이미지 파일 팝업
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 fn_uploadReport(btn){
Swal.fire({
icon: 'info',
title: '검사성적서 첨부',
text: '검사성적서 파일 업로드 기능은 추후 구현 예정입니다.'
});
}
// 부적합보고서 업로드
function fn_uploadNCR(btn){
Swal.fire({
icon: 'info',
title: '부적합보고서 첨부',
text: '부적합보고서 파일 업로드 기능은 추후 구현 예정입니다.'
// 공통 콜백 함수 (파일 등록 팝업에서 호출)
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",
@@ -588,11 +780,42 @@ function fn_loadData(objid, 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 부여
result.leftData.forEach(function(item, idx){
if(!item.ROW_ID) item.ROW_ID = item.OBJID || ("EXIST_" + (idx + 1));
rowSeq = Math.max(rowSeq, idx + 1);
});
leftGrid.setData(result.leftData);
}
if(result && result.rightData && result.rightData.length > 0){
rightGrid.setData(result.rightData);
// 불량 데이터를 좌측 행별로 분류
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;
allDefectData[parentId].push(defect);
});
}
console.log("불량 데이터 분류 완료:", allDefectData);
}
},
error: function(xhr, status, error){
@@ -601,26 +824,35 @@ function fn_loadData(objid, inspectionGroupId){
});
}
// =====================================================
// 저장
// =====================================================
function fn_save(){
var leftData = leftGrid.getData();
var rightData = rightGrid.getData();
// 현재 우측 데이터 저장
fn_saveRightGridData();
// 데이터 검증
if(leftData.length == 0 && rightData.length == 0){
Swal.fire({
icon: 'warning',
title: '알림',
text: '저장할 데이터가 없습니다.'
});
var leftData = leftGrid.getData();
if(leftData.length == 0){
Swal.fire({ icon: 'warning', title: '알림', text: '저장할 양품 정보가 없습니다.' });
return;
}
// 모든 불량 데이터 수집
var rightData = [];
Object.keys(allDefectData).forEach(function(leftRowId){
var defects = allDefectData[leftRowId] || [];
defects.forEach(function(defect){
defect.PARENT_ROW_ID = leftRowId;
rightData.push(defect);
});
});
// 저장 확인
Swal.fire({
icon: 'question',
title: '저장 확인',
text: '입력한 내용을 저장하시겠습니까?',
text: '양품 ' + leftData.length + '건, 불량 ' + rightData.length + '건을 저장하시겠습니까?',
showCancelButton: true,
confirmButtonText: '저장',
cancelButtonText: '취소'
@@ -628,7 +860,8 @@ function fn_save(){
if(result.isConfirmed){
var paramData = {
leftData: JSON.stringify(leftData),
rightData: JSON.stringify(rightData)
rightData: JSON.stringify(rightData),
INSPECTION_GROUP_ID: currentInspectionGroupId || "" // 수정 모드일 때 기존 그룹 ID 전달
};
$.ajax({
@@ -641,10 +874,8 @@ function fn_save(){
Swal.fire({
icon: 'success',
title: '저장 완료',
text: '저장되었습니다.',
confirmButtonText: '확인'
text: '저장되었습니다.'
}).then(function(){
// 부모 창의 리스트 새로고침
if(window.opener && window.opener.fn_search){
window.opener.fn_search();
}