V2025120101 #100

Merged
hjjeong merged 3 commits from V2025120101 into main 2025-12-16 07:12:33 +00:00
11 changed files with 2288 additions and 102 deletions

View File

@@ -73,12 +73,20 @@ $(document).ready(function(){
//fnc_productUPGNEWList(this.value,"","upg_no", "");
fn_UnitCodeList(this.value, "unit_code", "");
});
// 매입마감 버튼 클릭
$("#btnClose").click(function(){
fn_purchaseClose();
});
fn_search();
});
var columns = [
// 요구사항: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 발주수량, 입고수량, 미입고수량, 검사성적서, 입고결과
{title:'TOTAL_SUPPLY_PRICE' ,field:'TOTAL_SUPPLY_PRICE' ,visible:false, frozen:true},
{title:'TOTAL_DELIVERY_PRICE' ,field:'TOTAL_DELIVERY_PRICE' ,visible:false, frozen:true},
{headerHozAlign : 'center', hozAlign : 'center', minWidth : 130, widthGrow : 1, title : '품의서 No', field : 'PROPOSAL_NO',
formatter: fnc_createGridAnchorTag,
cellClick : function(e, cell) {
@@ -105,7 +113,7 @@ var columns = [
{headerHozAlign : 'center', hozAlign : 'right', minWidth : 90, widthGrow : 1, title : '미입고수량', field : 'NON_DELIVERY_QTY',
formatter:"money", formatterParams:{thousand:",", symbolAfter:"p", precision:false }
},
{headerHozAlign : 'center', hozAlign : 'center', minWidth : 100, widthGrow : 1, title : '검사성적서', field : 'INSPECTION_FILE_CNT',
{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);
@@ -120,7 +128,8 @@ var columns = [
var purchaseOrderNo = fnc_checkNull(cell.getData().PURCHASE_ORDER_NO);
fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS);
}
}
},
{headerHozAlign : 'center', hozAlign : 'center', minWidth : 140, widthGrow : 1, title : '매입마감', field : 'PURCHASE_CLOSE_DATE'}
];
//var grid;
@@ -140,7 +149,56 @@ function fn_search(){
value: selectedIdValues
}).appendTo('#form1');
// 그리드 조회
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/purchaseOrder/deliveryMngAcceptanceGridList.do", columns, true);
// 데이터 렌더링 완료 후 합계 계산 및 높이 조절
if(_tabulGrid) {
_tabulGrid.off("renderComplete");
_tabulGrid.on("renderComplete", function(){
fn_calculateTotalAmount();
// 그리드 렌더링 완료 후 높이 계산
fnc_calculateContentHeight("gridDiv", 30);
});
}
// 윈도우 리사이즈 이벤트 (한 번만 등록)
$(window).off("resize.gridHeight").on("resize.gridHeight", function() {
fnc_calculateContentHeight("gridDiv", 30);
});
}
// 발주금액 합계 계산 (그리드 데이터 기반)
function fn_calculateTotalAmount(){
if(!_tabulGrid) {
$("#totalOrderAmount").text("0");
$("#deliveredAmount").text("0");
$("#notDeliveredAmount").text("0");
return;
}
// 현재 그리드에 표시된 데이터 가져오기
var data = _tabulGrid.getData();
var totalOrderAmount = 0; // 총 발주금액
var totalDeliveredAmount = 0; // 입고금액
var totalNotDeliveredAmount = 0; // 미입고금액
if(data.length > 0) {
data.forEach(function(row) {
var rowTotalPrice = parseFloat(row.TOTAL_SUPPLY_PRICE || 0);
var rowDeliveredPrice = parseFloat(row.TOTAL_DELIVERY_PRICE || 0);
var rowNotDeliveredPrice = rowTotalPrice - rowDeliveredPrice;
totalOrderAmount += rowTotalPrice;
totalDeliveredAmount += rowDeliveredPrice;
totalNotDeliveredAmount += rowNotDeliveredPrice;
});
}
// 합계 표시
$("#totalOrderAmount").text(Number(Math.round(totalOrderAmount)).toLocaleString());
$("#deliveredAmount").text(Number(Math.round(totalDeliveredAmount)).toLocaleString());
$("#notDeliveredAmount").text(Number(Math.round(totalNotDeliveredAmount)).toLocaleString());
}
//수입검사등록
@@ -303,8 +361,69 @@ function fn_FileRegist(objId, docType, docTypeName){
fn_centerPopup(popup_width, popup_height, url);
}
// 매입마감 처리
function fn_purchaseClose(){
var selected = _tabulGrid.getSelectedData();
if(selected.length == 0){
Swal.fire("선택된 데이터가 없습니다.");
return;
}
// 이미 마감된 건 체크
var alreadyClosed = selected.filter(function(item){
return fnc_checkNull(item.PURCHASE_CLOSE_DATE) != '';
});
if(alreadyClosed.length > 0){
Swal.fire("이미 매입마감 처리된 건이 포함되어 있습니다.");
return;
}
// 입고완료 건만 마감 가능
var notCompleted = selected.filter(function(item){
return item.DELIVERY_STATUS != '입고완료';
});
if(notCompleted.length > 0){
Swal.fire("입고완료 건만 매입마감 처리가 가능합니다.");
return;
}
Swal.fire({
title: '매입마감',
text: '선택한 ' + selected.length + '건을 매입마감 처리하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonText: '확인',
cancelButtonText: '취소'
}).then((result) => {
if(result.isConfirmed){
var objIds = selected.map(function(item){
return item.OBJID;
}).join(',');
$.ajax({
url: '/purchaseOrder/purchaseClose.do',
type: 'POST',
data: { objIds: objIds },
dataType: 'json',
success: function(response){
if(response.result == 'SUCCESS'){
Swal.fire('매입마감 처리가 완료되었습니다.').then(function(){
fn_search();
});
} else {
Swal.fire('매입마감 처리 중 오류가 발생했습니다.');
}
},
error: function(){
Swal.fire('서버 통신 중 오류가 발생했습니다.');
}
});
}
});
}
</script>
<body>
<body class="bodyNoScroll">
<form name="hiddenForm" id="hiddenForm" method="post">
<input type="hidden" name="checkArr" id="checkArr">
<input type="hidden" name="PURCHASE_ORDER_MASTER_OBJID" id="PURCHASE_ORDER_MASTER_OBJID">
@@ -324,6 +443,7 @@ function fn_FileRegist(objId, docType, docTypeName){
<div class="btnArea">
<input type="button" class="plm_btns" value="조회" id="btnSearch">
<input type="button" class="plm_btns" value="입고등록" id="btnAccept">
<input type="button" class="plm_btns" value="매입마감" id="btnClose">
<%-- <input type="button" class="plm_btns" value="부적합등록" id="btnInvaild"> --%>
</div>
</div>
@@ -398,6 +518,15 @@ function fn_FileRegist(objId, docType, docTypeName){
</table>
</div>
<!-- 발주금액 합계 표시 영역 -->
<div style="padding:5px 10px; background: #f5f5f5;">
<span style="font-weight: bold; font-size: 13px;">
발주 금액: <span id="totalOrderAmount" style="color: #4CAF50;">0</span> 원
(입고: <span id="deliveredAmount" style="color: #2196F3;">0</span> 원,
미입고: <span id="notDeliveredAmount" style="color: #FF9800;">0</span> 원)
</span>
</div>
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
</div>
</div>

View File

@@ -8,7 +8,7 @@
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<%
String menuObjId = request.getParameter("menuObjId");
String menuName = CommonUtils.getMenuName(menuObjId, "수입검사 관리");
String menuName = CommonUtils.getMenuName(menuObjId, "수입검사 요청");
%>
<!DOCTYPE html>
<html>
@@ -71,9 +71,9 @@ $(document).ready(function(){
fn_search();
});
// 수입검사 등록
$("#btnRegist").click(function(){
fn_registPopUp();
// 수입검사 요청
$("#btnRequest").click(function(){
fn_requestPopUp();
});
// 엑셀 다운로드
@@ -84,42 +84,39 @@ $(document).ready(function(){
fn_search();
});
// 컬럼: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 입고결과, 검사자, 검사일, 검사결과
// 컬럼: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 입고결과, 요청일, 요청자
var columns = [
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'품의서 No', field:'PROPOSAL_NO',
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().PROPOSAL_OBJID);
if(objId != '') fn_openProposalPopUp(objId);
}
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'품의서 No', field:'PROPOSAL_NO'
// formatter:fnc_createGridAnchorTag,
// cellClick:function(e, cell){
// var objId = fnc_checkNull(cell.getData().PROPOSAL_OBJID);
// if(objId != '') fn_openProposalPopUp(objId);
// }
},
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'발주서 No', field:'PURCHASE_ORDER_NO',
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().OBJID);
fn_formPopUp(objId);
}
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'발주서 No', field:'PURCHASE_ORDER_NO'
// formatter:fnc_createGridAnchorTag,
// cellClick:function(e, cell){
// var objId = fnc_checkNull(cell.getData().OBJID);
// fn_formPopUp(objId);
// }
},
{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:2, title:'공급업체', field:'PARTNER_NAME'},
{headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'입고결과', field:'DELIVERY_STATUS'},
{headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'검사자', field:'INSPECTOR_NAME'},
{headerHozAlign:'center', hozAlign:'center', minWidth:100, widthGrow:1, title:'검사일', field:'INSPECTION_DATE'},
{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 '<span class="inspection-ng">NG</span>';
if(val === 'OK') return '<span class="inspection-ok">OK</span>';
return val;
},
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().OBJID);
var result = fnc_checkNull(cell.getData().INSPECTION_RESULT);
if(result != '') fn_resultPopUp(objId);
}
}
{headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'입고결과', field:'DELIVERY_STATUS',
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().OBJID);
var DELIVERY_STATUS = fnc_checkNull(cell.getData().DELIVERY_STATUS);
fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS);
}
},
{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_YN'},
{headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'요청현황', field:'REQUEST_STATUS'}
];
// 조회
@@ -136,8 +133,8 @@ function fn_search(){
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/quality/incomingInspectionGridList.do", columns, true);
}
// 수입검사 등록 팝업
function fn_registPopUp(){
// 수입검사 요청 팝업
function fn_requestPopUp(){
var selected = _tabulGrid.getSelectedData();
if(selected.length == 0){
Swal.fire("선택된 데이터가 없습니다.");
@@ -148,11 +145,11 @@ function fn_registPopUp(){
return;
}
var popup_width = 1700;
var popup_height = 700;
var popup_width = 1200;
var popup_height = 600;
var hiddenForm = document.hiddenForm;
var target = "incomingInspectionPopUp";
var url = "/quality/incomingInspectionFormPopUp.do";
var target = "incomingInspectionRequestPopUp";
var url = "/quality/incomingInspectionRequestPopUp.do";
fn_centerPopup(popup_width, popup_height, "", target);
@@ -163,23 +160,6 @@ function fn_registPopUp(){
hiddenForm.submit();
}
// 검사결과 확인/수정 팝업
function fn_resultPopUp(objId){
var popup_width = 1700;
var popup_height = 700;
var hiddenForm = document.hiddenForm;
var target = "inspectionResultPopUp";
var url = "/quality/incomingInspectionFormPopUp.do";
fn_centerPopup(popup_width, popup_height, "", target);
hiddenForm.action = url;
hiddenForm.OBJID.value = objId;
hiddenForm.actionType.value = 'view';
hiddenForm.target = target;
hiddenForm.submit();
}
// 품의서 팝업
function fn_openProposalPopUp(objId){
var popup_width = 1200;
@@ -211,6 +191,23 @@ function fn_excelDownload(){
form.submit();
form.action = "";
}
function fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS){
var popup_width = 1560;
var popup_height = 1050;
var hiddenForm = document.hiddenForm;
var target = "deliveryAcceptancePopUp";
var url = "/purchaseOrder/deliveryAcceptanceFormPopUp_new.do";
fn_centerPopup(popup_width, popup_height, "", target);
hiddenForm.action = url;
hiddenForm.PURCHASE_ORDER_MASTER_OBJID.value = objId;
hiddenForm.DELIVERY_STATUS.value = DELIVERY_STATUS;
hiddenForm.actionType.value = 'view'; // 뷰 전용으로 설정
hiddenForm.target = target;
hiddenForm.submit();
}
</script>
<body>
@@ -218,6 +215,7 @@ function fn_excelDownload(){
<input type="hidden" name="OBJID" id="OBJID">
<input type="hidden" name="PURCHASE_ORDER_NO" id="PURCHASE_ORDER_NO">
<input type="hidden" name="PURCHASE_ORDER_MASTER_OBJID" id="PURCHASE_ORDER_MASTER_OBJID">
<input type="hidden" name="DELIVERY_STATUS" id="DELIVERY_STATUS">
<input type="hidden" name="actionType" id="actionType">
</form>
@@ -229,7 +227,7 @@ function fn_excelDownload(){
<span><%=menuName%></span>
</h2>
<div class="btnArea">
<input type="button" class="plm_btns" value="수입검사 등록" id="btnRegist">
<input type="button" class="plm_btns" value="수입검사 요청" id="btnRequest">
<input type="button" class="plm_btns" value="조회" id="btnSearch">
<input type="button" class="plm_btns excel" value="Excel Download" id="btnExcel">
</div>
@@ -272,22 +270,13 @@ function fn_excelDownload(){
</select>
</td>
<td><label for="">검사자</label></td>
<td><select name="inspector_id" id="inspector_id" class="select2" autocomplete="off" style="width:120px;"><option value="">선택</option>${code_map.inspector_id}</select></td>
<td><label for="">요청자</label></td>
<td><select name="request_user_id" id="request_user_id" class="select2" autocomplete="off" style="width:120px;"><option value="">선택</option>${code_map.request_user_id}</select></td>
<td class=""><label>검사일</label></td>
<td class=""><label>요청일</label></td>
<td>
<input type="text" name="inspection_start_date" id="inspection_start_date" style="width:100px;" autocomplete="off" value="${param.inspection_start_date}" class="date_icon">~
<input type="text" name="inspection_end_date" id="inspection_end_date" style="width:100px;" autocomplete="off" value="${param.inspection_end_date }" class="date_icon">
</td>
<td><label for="">검사결과</label></td>
<td>
<select name="search_inspection_result" id="search_inspection_result" class="select2" autocomplete="off" style="width:120px;">
<option value="">전체</option>
<option value="OK">OK</option>
<option value="NG">NG</option>
</select>
<input type="text" name="request_start_date" id="request_start_date" style="width:100px;" autocomplete="off" value="${param.request_start_date}" class="date_icon">~
<input type="text" name="request_end_date" id="request_end_date" style="width:100px;" autocomplete="off" value="${param.request_end_date }" class="date_icon">
</td>
</tr>
</table>

View File

@@ -0,0 +1,348 @@
<%@ 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" %>
<c:set var="now" value="<%=new java.util.Date() %>"/>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<%
String menuObjId = request.getParameter("menuObjId");
String menuName = CommonUtils.getMenuName(menuObjId, "수입검사 진행");
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
</head>
<style>
.select2-selection__choice {
font-size: 11px;
background-color: #fff !important;
border: none !important;
margin-right: 0px !important;
}
.select2-selection__choice__remove {
display: contents !important;
}
.select2-container .select2-selection--multiple {
min-height: 20px !important;
}
.select2-container--default .select2-selection--multiple .select2-selection__choice {
margin-top: 3.5px !important;
}
.select2-selection__rendered {
height: 18px !important;
}
.select2-container .select2-selection--multiple .select2-selection__rendered {
overflow: auto !important;
}
/* 검사결과 스타일 */
.inspection-ng { color: #dc3545; font-weight: bold; cursor: pointer; }
.inspection-ok { color: #28a745; font-weight: bold; cursor: pointer; }
.inspection-ng:hover, .inspection-ok:hover { text-decoration: underline; }
body, html {
overflow-x: hidden;
width: 100%;
margin: 0;
padding: 0;
}
</style>
<script type="text/javascript">
$(document).ready(function(){
fnc_changePaginationAndTotalCountArea();
$('.select2').select2();
fnc_datepick();
$("input").keyup(function(e){
if(e.keyCode == 13){
$("#page").val("1");
fn_search();
}
});
// 조회
$("#btnSearch").click(function(){
$("#page").val("1");
fn_search();
});
// 수입검사 진행
$("#btnInspection").click(function(){
fn_inspectionPopUp();
});
// 엑셀 다운로드
$("#btnExcel").click(function(){
fn_excelDownload();
});
fn_search();
});
// 컬럼: 품의서 No, 발주서 No, 프로젝트번호, 품번, 품명, 공급업체, 입고결과, 요청일, 요청자, 검사자, 검사일, 검사결과
var columns = [
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'품의서 No', field:'PROPOSAL_NO'
// formatter:fnc_createGridAnchorTag,
// cellClick:function(e, cell){
// var objId = fnc_checkNull(cell.getData().PROPOSAL_OBJID);
// if(objId != '') fn_openProposalPopUp(objId);
// }
},
{headerHozAlign:'center', hozAlign:'center', minWidth:120, widthGrow:1, title:'발주서 No', field:'PURCHASE_ORDER_NO'
// formatter:fnc_createGridAnchorTag,
// cellClick:function(e, cell){
// var objId = fnc_checkNull(cell.getData().OBJID);
// fn_formPopUp(objId);
// }
},
{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:2, title:'공급업체', field:'PARTNER_NAME'},
{headerHozAlign:'center', hozAlign:'center', minWidth:80, widthGrow:1, title:'입고결과', field:'DELIVERY_STATUS',
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().OBJID);
var DELIVERY_STATUS = fnc_checkNull(cell.getData().DELIVERY_STATUS);
fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS);
}
},
// {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',
formatter: function(cell, formatterParams, onRendered){
var val = fnc_checkNull(cell.getValue());
if(val === 'NG') return '<span class="inspection-ng">NG</span>';
if(val === 'OK') return '<span class="inspection-ok">OK</span>';
if(val === '검사중') return '<span style="color:#ffc107;font-weight:bold;">검사중</span>';
return '<span style="color:#999;cursor:pointer;">미검사</span>';
},
cellClick:function(e, cell){
var objId = fnc_checkNull(cell.getData().OBJID);
fn_inspectionPopUp(objId);
}
}
];
// 조회
function fn_search(){
// 프로젝트번호 multi select 처리
var selectedProjectNos = $("#project_no").val();
$('input[name="project_nos"]').remove();
$('<input>').attr({
type: 'hidden',
name: 'project_nos',
value: selectedProjectNos
}).appendTo('#form1');
// 수입검사 진행 리스트는 요청완료 건만 조회
$('input[name="request_status_filter"]').remove();
$('<input>').attr({
type: 'hidden',
name: 'request_status_filter',
value: '요청완료'
}).appendTo('#form1');
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/quality/incomingInspectionGridList.do", columns, true);
}
// 수입검사 진행 팝업
function fn_inspectionPopUp(objId){
var selected = [];
if(objId){
// 검사결과 클릭으로 호출된 경우
selected = [{OBJID: objId}];
} else {
// 버튼 클릭으로 호출된 경우
selected = _tabulGrid.getSelectedData();
if(selected.length == 0){
Swal.fire("선택된 데이터가 없습니다.");
return;
}
if(selected.length > 1){
Swal.fire("한건씩 등록 가능합니다.");
return;
}
}
var popup_width = 1800;
var popup_height = 700;
var hiddenForm = document.hiddenForm;
var target = "incomingInspectionProgressPopUp";
var url = "/quality/incomingInspectionProgressPopUp.do";
fn_centerPopup(popup_width, popup_height, "", target);
hiddenForm.action = url;
hiddenForm.OBJID.value = selected[0].OBJID;
hiddenForm.target = target;
hiddenForm.submit();
}
// 품의서 팝업
function fn_openProposalPopUp(objId){
var popup_width = 1200;
var popup_height = 900;
var url = "/salesMng/proposalFormPopUp.do?PROPOSAL_OBJID=" + objId;
window.open(url, "proposalFormPopUp", "width="+popup_width+",height="+popup_height+",scrollbars=yes,resizable=yes");
}
// 발주서 팝업
function fn_formPopUp(objId){
var popup_width = 1460;
var popup_height = 1050;
var hiddenForm = document.hiddenForm;
var target = "purchaseOrderFormPopup_new";
var url = "/purchaseOrder/purchaseOrderFormPopup_new.do";
fn_centerPopup(popup_width, popup_height, "", target);
hiddenForm.PURCHASE_ORDER_MASTER_OBJID.value = objId;
hiddenForm.action = url;
hiddenForm.target = target;
hiddenForm.submit();
}
// 엑셀 다운로드
function fn_excelDownload(){
var form = document.form1;
form.action = "/quality/incomingInspectionExcelDownload.do";
form.submit();
form.action = "";
}
// 첨부파일 팝업
function fn_FileRegist(objId, docType, docTypeName){
var popup_width = 800;
var popup_height = 300;
var objId = objId;
var docType = docType;
var docTypeName = docTypeName;
var params = "?targetObjId="+objId+"&docType="+docType+"&docTypeName="+docTypeName;
var url = "/projectConcept/FileRegistPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
}
function fn_deliveryAcceptanceViewPopUp(objId,DELIVERY_STATUS){
var popup_width = 1560;
var popup_height = 1050;
var hiddenForm = document.hiddenForm;
var target = "deliveryAcceptancePopUp";
var url = "/purchaseOrder/deliveryAcceptanceFormPopUp_new.do";
fn_centerPopup(popup_width, popup_height, "", target);
hiddenForm.action = url;
hiddenForm.PURCHASE_ORDER_MASTER_OBJID.value = objId;
hiddenForm.DELIVERY_STATUS.value = DELIVERY_STATUS;
hiddenForm.actionType.value = 'view'; // 뷰 전용으로 설정
hiddenForm.target = target;
hiddenForm.submit();
}
</script>
<body>
<form name="hiddenForm" id="hiddenForm" method="post">
<input type="hidden" name="OBJID" id="OBJID">
<input type="hidden" name="PURCHASE_ORDER_NO" id="PURCHASE_ORDER_NO">
<input type="hidden" name="PURCHASE_ORDER_MASTER_OBJID" id="PURCHASE_ORDER_MASTER_OBJID">
<input type="hidden" name="DELIVERY_STATUS" id="DELIVERY_STATUS">
<input type="hidden" name="actionType" id="actionType">
</form>
<form name="form1" id="form1" action="" method="post">
<div class="content-box" style="height: 99.3%;">
<div class="content-box-s">
<div class="plm_menu_name_gdnsi">
<h2>
<span><%=menuName%></span>
</h2>
<div class="btnArea">
<input type="button" class="plm_btns" value="수입검사 진행" id="btnInspection">
<input type="button" class="plm_btns" value="조회" id="btnSearch">
<input type="button" class="plm_btns excel" value="Excel Download" id="btnExcel">
</div>
</div>
<div id="plmSearchZon">
<table>
<tr>
<td><label for="">품의서 No</label></td>
<td><input type="text" name="search_proposal_no" id="search_proposal_no" style="width:120px;" autocomplete="off" value="${param.search_proposal_no}"></td>
<td><label for="">발주서 No</label></td>
<td><input type="text" name="search_purchase_order_no" id="search_purchase_order_no" style="width:120px;" autocomplete="off" value="${param.search_purchase_order_no}"></td>
<td><label for="">프로젝트번호</label></td>
<td>
<select name="project_no" id="project_no" style="width:210px;" class="select2" autocomplete="off" multiple="multiple">
<option value="">선택</option>
${code_map.project_no}
</select>
</td>
<td><label for="">품번</label></td>
<td><input type="text" name="search_part_no" id="search_part_no" style="width:170px;" autocomplete="off" value="${param.search_part_no}"></td>
<td><label for="">품명</label></td>
<td><input type="text" name="search_part_name" id="search_part_name" style="width:170px;" autocomplete="off" value="${param.search_part_name}"></td>
<td><label for="">공급업체</label></td>
<td><select name="search_partner" id="search_partner" class="select2" autocomplete="off" style="width:250px;"><option value="">선택</option>${code_map.partner_objid}</select></td>
</tr>
<tr>
<td><label for="">입고결과</label></td>
<td>
<select name="search_delivery_status" id="search_delivery_status" class="select2" autocomplete="off" style="width:120px;">
<option value="">전체</option>
<option value="입고중">입고중</option>
<option value="입고완료">입고완료</option>
</select>
</td>
<td><label for="">검사자</label></td>
<td><select name="inspector_id" id="inspector_id" class="select2" autocomplete="off" style="width:120px;"><option value="">선택</option>${code_map.inspector_id}</select></td>
<td class=""><label>검사일</label></td>
<td>
<input type="text" name="inspection_start_date" id="inspection_start_date" style="width:100px;" autocomplete="off" value="${param.inspection_start_date}" class="date_icon">~
<input type="text" name="inspection_end_date" id="inspection_end_date" style="width:100px;" autocomplete="off" value="${param.inspection_end_date }" class="date_icon">
</td>
<td><label for="">검사결과</label></td>
<td>
<select name="search_inspection_result" id="search_inspection_result" class="select2" autocomplete="off" style="width:120px;">
<option value="">전체</option>
<option value="OK">OK</option>
<option value="NG">NG</option>
</select>
</td>
</tr>
</table>
</div>
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
</div>
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,844 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<%
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
String connector = 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>
</head>
<style>
.form-header {
display: flex;
align-items: center;
gap: 20px;
padding: 10px 15px;
background: #f8f9fa;
border-bottom: 1px solid #ddd;
}
.form-header label {
font-weight: bold;
margin-right: 5px;
}
.form-header .required {
color: red;
}
.form-header input, .form-header select {
padding: 5px 10px;
border: 1px solid #ccc;
border-radius: 3px;
}
.form-header .info-text {
color: #666;
font-size: 12px;
}
/* 좌/우 분할 레이아웃 */
.split-container {
display: flex;
height: calc(100vh - 180px);
gap: 10px;
padding: 10px;
}
.left-panel {
width: 35%;
display: flex;
flex-direction: column;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.right-panel {
width: 65%;
display: flex;
flex-direction: column;
border: 1px solid #ddd;
border-radius: 4px;
overflow: hidden;
}
.panel-header {
background: #4a5568;
color: white;
padding: 10px 15px;
font-weight: bold;
display: flex;
justify-content: space-between;
align-items: center;
}
.panel-header .btn-group button {
padding: 4px 10px;
margin-left: 5px;
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
}
.panel-header .btn-add {
background: #48bb78;
color: white;
}
.panel-header .btn-delete {
background: #e53e3e;
color: white;
}
.panel-body {
flex: 1;
overflow: auto;
}
.grid-container {
height: 100%;
}
/* 선택된 행 하이라이트 */
.tabulator-row.selected-row {
background-color: #e3f2fd !important;
}
/* 스킵 행 스타일 */
.tabulator-row.skip-row {
background-color: #f5f5f5 !important;
color: #999 !important;
}
/* 비활성화 안내 */
.disabled-notice {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
color: #999;
font-size: 14px;
}
#gridDiv {
height: 0px !important;
}
</style>
<script type="text/javascript">
var _editable = true;
var leftGrid; // 입고품목 그리드 (좌측)
var rightGrid; // 불량상세 그리드 (우측)
var selectedDetailObjid = null; // 현재 선택된 입고품목의 OBJID
var selectedRowData = null; // 현재 선택된 입고품목 데이터
// 불량유형 목록 (0001820)
var _DEFECT_TYPE_LIST = [];
// 처리현황 목록
var _ACTION_STATUS_LIST = [
{"CODE": "", "NAME": "선택"},
{"CODE": "Rework", "NAME": "Rework"},
{"CODE": "Scrap", "NAME": "Scrap"}
];
// 처리결과 목록
var _ACTION_RESULT_LIST = [
{"CODE": "", "NAME": "선택"},
{"CODE": "수정", "NAME": "수정"},
{"CODE": "폐기", "NAME": "폐기"},
{"CODE": "특채", "NAME": "특채"}
];
// 검사자 목록
var _INSPECTOR_LIST = [];
// 검사결과 목록
var _INSPECTION_RESULT_LIST = [
{"CODE": "", "NAME": "선택"},
{"CODE": "OK", "NAME": "OK"},
{"CODE": "NG", "NAME": "NG"}
];
$(document).ready(function(){
fnc_datepick();
$('.select2').select2();
// 불량유형 목록 조회
_DEFECT_TYPE_LIST = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getCodeselect", "code": "0001820"})
);
// 검사자 목록 조회
_INSPECTOR_LIST = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getUserselect"})
);
// 검사일 기본 설정 (저장된 값 없으면 오늘 날짜)
var savedInspectionDate = "${info.INSPECTION_DATE}";
if(savedInspectionDate != '' && savedInspectionDate != 'null'){
$("#INSPECTION_DATE").val(savedInspectionDate);
} else if($("#INSPECTION_DATE").val() == ''){
$("#INSPECTION_DATE").val("${today}");
}
// 저장 버튼
$("#btnSave").click(function(){
fn_save();
});
// 닫기 버튼
$("#btnClose").click(function(){
self.close();
});
// 행 추가 버튼
$("#btnAddRow").click(function(){
fn_addDefectRow();
});
// 행 삭제 버튼
$("#btnDeleteRow").click(function(){
fn_deleteDefectRow();
});
// 그리드 초기화 및 조회
fn_initLeftGrid();
fn_initRightGrid();
fn_searchLeftGrid();
});
// =====================================================
// 좌측 그리드 (입고품목) 초기화
// =====================================================
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: fnc_createGridAnchorTag,
cellClick: function(e, cell) {
// 품번 클릭 시 품목상세 팝업
if(e.target.tagName === 'A') {
fn_openPartDetailPopUp(cell.getData().PART_OBJID);
}
}
},
{title:'품명', field:'PART_NAME', headerHozAlign:'center', hozAlign:'left', width:150},
{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,
formatter: function(cell) {
var val = cell.getValue();
if(val === '검사') return '<span style="color:#007bff;font-weight:bold;">검사</span>';
if(val === '스킵') return '<span style="color:#999;">스킵</span>';
return val;
}
}
];
leftGrid = new Tabulator("#leftGrid", {
layout: "fitDataStretch",
height: "99.6%",
placeholder: "데이터가 없습니다.",
columns: columns,
selectable: 1, // 단일 선택
rowFormatter: function(row) {
var data = row.getData();
// 스킵인 행은 회색 처리
if(data.INSPECTION_YN === '스킵') {
row.getElement().classList.add('skip-row');
}
}
});
// 행 선택 이벤트
leftGrid.on("rowSelected", function(row) {
var data = row.getData();
selectedDetailObjid = data.INSPECTION_DETAIL_OBJID; // INCOMING_INSPECTION_DETAIL.OBJID
selectedRowData = data;
// 검사여부가 '검사'인 경우만 우측 그리드 활성화
if(data.INSPECTION_YN === '검사' && data.INSPECTION_DETAIL_OBJID) {
fn_enableRightGrid();
fn_searchRightGrid(data.INSPECTION_DETAIL_OBJID);
} else {
fn_disableRightGrid();
}
});
// 행 선택 해제 이벤트
leftGrid.on("rowDeselected", function(row) {
selectedDetailObjid = null;
selectedRowData = null;
fn_clearRightGrid();
});
// 셀 편집 이벤트
leftGrid.on("cellEdited", function(cell) {
cell.getData().GRID_STATUS = 'U';
});
}
// =====================================================
// 우측 그리드 (불량상세) 초기화
// =====================================================
function fn_initRightGrid() {
var columns = [
{formatter:"rowSelection", titleFormatter:"rowSelection", headerHozAlign:"center", hozAlign:"center", headerSort:false, width:30},
{title:'검사구분', field:'INSPECTION_TYPE', headerHozAlign:'center', hozAlign:'center', width:80,
editor:"select",
editorParams:{values:{"":"선택", "원자재":"원자재", "외주가공":"외주가공"}},
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
return val;
}
},
{title:'불량유형', field:'DEFECT_TYPE', headerHozAlign:'center', hozAlign:'center', width:100,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_DEFECT_TYPE_LIST});
},
editorParams: {valueId:"CODE", labelId:"NAME", values:_DEFECT_TYPE_LIST}
},
{title:'불량원인', field:'DEFECT_REASON', headerHozAlign:'center', hozAlign:'center', width:100,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
var defectType = cell.getData().DEFECT_TYPE;
if (defectType) {
var reasonList = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getCodeselect", "code": defectType})
);
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values: reasonList});
}
return '';
},
editorParams: function(cell) {
var defectType = cell.getData().DEFECT_TYPE;
if (defectType) {
var reasonList = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getCodeselect", "code": defectType})
);
return {valueId:"CODE", labelId:"NAME", values: reasonList};
}
return {valueId:"CODE", labelId:"NAME", values: [{"CODE": "", "NAME": "선택"}]};
}
},
{title:'처리현황', field:'ACTION_STATUS', headerHozAlign:'center', hozAlign:'center', width:80,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_ACTION_STATUS_LIST});
},
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:'DEFECT_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_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:"input"
},
{title:'검사자', field:'INSPECTOR_ID', headerHozAlign:'center', hozAlign:'center', width:100,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_INSPECTOR_LIST});
},
editorParams: {valueId:"CODE", labelId:"NAME", values:_INSPECTOR_LIST}
},
{title:'처리결과', field:'ACTION_RESULT', headerHozAlign:'center', hozAlign:'center', width:80,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if(!val || val === '') return '';
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_ACTION_RESULT_LIST});
},
editorParams: {valueId:"CODE", labelId:"NAME", values:_ACTION_RESULT_LIST}
},
{title:'검사결과', field:'INSPECTION_RESULT', headerHozAlign:'center', hozAlign:'center', width:80,
editor: fnc_customSelectEditor,
formatter: function(cell) {
var val = cell.getValue();
if (val === 'OK') return '<span style="color:#28a745;font-weight:bold;">OK</span>';
if (val === 'NG') return '<span style="color:#dc3545;font-weight:bold;">NG</span>';
if(!val || val === '') return '';
},
editorParams: {valueId:"CODE", labelId:"NAME", values:_INSPECTION_RESULT_LIST}
},
{title:'이미지파일', field:'IMAGE_FILE_CNT', headerHozAlign:'center', hozAlign:'center', width:90,
formatter: fnc_subInfoValueFormatter,
cellClick: function(e, cell) {
var objId = fnc_checkNull(cell.getData().OBJID);
if(objId) {
fn_openImageFilePopUp(objId);
} else {
Swal.fire("먼저 저장 후 이미지를 등록할 수 있습니다.");
}
}
},
{title:'첨부파일', field:'ATTACH_FILE_CNT', headerHozAlign:'center', hozAlign:'center', width:80,
formatter: fnc_subInfoValueFormatter,
cellClick: function(e, cell) {
var objId = fnc_checkNull(cell.getData().OBJID);
if(objId) {
fn_openAttachFilePopUp(objId);
} else {
Swal.fire("먼저 저장 후 파일을 등록할 수 있습니다.");
}
}
},
{title:'비고', field:'REMARK', headerHozAlign:'center', hozAlign:'left', width:150,
editor:"input"
}
];
rightGrid = new Tabulator("#rightGrid", {
layout: "fitDataStretch",
height: "99.6%",
placeholder: "좌측에서 품목을 선택하세요.",
columns: columns,
selectable: true
});
// 셀 편집 이벤트
rightGrid.on("cellEdited", function(cell) {
var row = cell.getRow();
var data = row.getData();
// 불량유형 변경 시 불량원인 초기화
if (cell.getField() === 'DEFECT_TYPE') {
row.update({"DEFECT_REASON": ""});
}
// 검사수량, 불량수량 변경 시 불량율 자동 계산
if (cell.getField() === 'INSPECTION_QTY' || cell.getField() === 'DEFECT_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"});
}
}
data.GRID_STATUS = data.GRID_STATUS === 'I' ? 'I' : 'U';
});
}
// 불량율 계산
function fn_calcDefectRate(row) {
var data = row.getData();
var inspectionQty = parseInt(data.INSPECTION_QTY) || 0;
var defectQty = parseInt(data.DEFECT_QTY) || 0;
if (inspectionQty > 0) {
var rate = (defectQty / inspectionQty * 100).toFixed(2);
row.update({"DEFECT_RATE": rate});
} else {
row.update({"DEFECT_RATE": ""});
}
}
// =====================================================
// 좌측 그리드 조회
// =====================================================
function fn_searchLeftGrid() {
var param = {
PURCHASE_ORDER_MASTER_OBJID: "${param.OBJID}",
INSPECTION_YN: "검사" // 검사여부가 '검사'인 항목만 조회
};
console.log("좌측 그리드 조회 파라미터:", param);
$.ajax({
url: "/quality/incomingInspectionDetailGridList.do",
type: "POST",
data: param,
dataType: "json",
success: function(data) {
console.log("좌측 그리드 조회 결과:", data);
if(data.RESULTLIST) {
leftGrid.setData(data.RESULTLIST);
}
},
error: function(xhr, status, error) {
console.error("좌측 그리드 조회 오류:", error);
}
});
}
// =====================================================
// 우측 그리드 조회 (불량상세)
// =====================================================
function fn_searchRightGrid(inspectionDetailObjid) {
if(!inspectionDetailObjid) {
rightGrid.setData([]);
return;
}
$.ajax({
url: "/quality/getIncomingInspectionDefectList.do",
type: "POST",
data: {INSPECTION_DETAIL_OBJID: inspectionDetailObjid},
dataType: "json",
success: function(data) {
if(data.list) {
rightGrid.setData(data.list);
} else {
rightGrid.setData([]);
}
}
});
}
// 우측 그리드 비활성화 (검사여부가 '검사'가 아닌 경우)
function fn_disableRightGrid() {
rightGrid.setData([]);
$("#rightGridContainer").html('<div class="disabled-notice">검사여부가 "검사"인 항목만 불량상세를 입력할 수 있습니다.</div>');
$("#btnAddRow, #btnDeleteRow").prop('disabled', true);
}
// 우측 그리드 활성화
function fn_enableRightGrid() {
if($("#rightGridContainer .disabled-notice").length > 0) {
$("#rightGridContainer").html('<div id="rightGrid" class="grid-container"></div>');
fn_initRightGrid();
}
$("#btnAddRow, #btnDeleteRow").prop('disabled', false);
}
// 우측 그리드 초기화
function fn_clearRightGrid() {
rightGrid.setData([]);
rightGrid.options.placeholder = "좌측에서 품목을 선택하세요.";
}
// =====================================================
// 불량상세 행 추가
// =====================================================
function fn_addDefectRow() {
if(!selectedDetailObjid) {
Swal.fire("좌측에서 품목을 먼저 선택하세요.");
return;
}
if(selectedRowData && selectedRowData.INSPECTION_YN === '스킵') {
Swal.fire("검사여부가 '스킵'인 항목은 불량상세를 추가할 수 없습니다.");
return;
}
// 새 행 데이터 (검사일: 오늘, 검사자: 접속자)
var newRow = {
OBJID: fnc_createObjId(),
INSPECTION_DETAIL_OBJID: selectedDetailObjid,
INSPECTION_TYPE: "",
INSPECTION_DATE: "${today}",
INSPECTOR_ID: "<%=connector%>",
DEFECT_TYPE: "",
DEFECT_REASON: "",
ACTION_STATUS: "",
ACTION_RESULT: "",
INSPECTION_QTY: selectedRowData ? selectedRowData.DELIVERY_QTY : 0, // 입고수량을 기본값으로
DEFECT_QTY: 0,
DEFECT_RATE: "",
INSPECTION_RESULT: "",
REMARK: "",
GRID_STATUS: "I"
};
rightGrid.addRow(newRow);
}
// =====================================================
// 불량상세 행 삭제
// =====================================================
function fn_deleteDefectRow() {
var selectedRows = rightGrid.getSelectedRows();
if(selectedRows.length === 0) {
Swal.fire("삭제할 행을 선택하세요.");
return;
}
Swal.fire({
title: '삭제 확인',
text: '선택한 ' + selectedRows.length + '개 행을 삭제하시겠습니까?',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: '삭제',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
selectedRows.forEach(function(row) {
var data = row.getData();
if(data.GRID_STATUS === 'I') {
// 신규 행은 바로 삭제
row.delete();
} else {
// 기존 행은 삭제 표시
data.GRID_STATUS = 'D';
row.update(data);
row.getElement().style.display = 'none';
}
});
}
});
}
// 품목상세 팝업
function fn_openPartDetailPopUp(partObjId) {
if (!partObjId) return;
var popup_width = 1200;
var popup_height = 900;
var url = "/partMng/partMngDetailPopUp.do?OBJID=" + partObjId;
window.open(url, "partMngDetailPopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
}
// 이미지 파일 팝업
function fn_openImageFilePopUp(objId) {
var popup_width = 650;
var popup_height = 550;
var url = "/common/ImageRegistPopup.do?targetObjId=" + objId + "&docType=INSPECTION_DEFECT_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', 'INSPECTION_DEFECT_IMAGE');
}
// 첨부파일 팝업
function fn_openAttachFilePopUp(objId) {
var popup_width = 800;
var popup_height = 300;
var params = "?targetObjId=" + objId + "&docType=INSPECTION_DEFECT_FILE&docTypeName=검사첨부";
var url = "/common/FileRegistPopup.do" + params;
var popup = window.open(url, "attachFilePopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
// 팝업 닫힘 감지하여 해당 행의 파일 카운트만 업데이트
fn_watchPopupClose(popup, objId, 'ATTACH_FILE_CNT', 'INSPECTION_DEFECT_FILE');
}
// 팝업 닫힘 감지 및 파일 카운트 업데이트
function fn_watchPopupClose(popup, objId, fieldName, docType) {
var checkPopup = setInterval(function() {
if(popup.closed) {
clearInterval(checkPopup);
// 파일 카운트 조회 후 해당 행만 업데이트
fn_updateRightGridFileCnt(objId, fieldName, docType);
}
}, 500);
}
// 우측 그리드 파일 카운트 업데이트
function fn_updateRightGridFileCnt(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);
}
});
}
});
}
// 공통 fnc_tabulCallbackFnc 오버라이드 (이 페이지에서는 rightGrid 사용)
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_save() {
// 유효성 검사
if($("#INSPECTION_DATE").val() == ''){
Swal.fire("검사일을 입력해주세요.");
$("#INSPECTION_DATE").focus();
return;
}
if($("#INSPECTOR_ID").val() == ''){
Swal.fire("검사자를 선택해주세요.");
$("#INSPECTOR_ID").focus();
return;
}
// 좌측 그리드 데이터 (입고품목)
var leftGridData = leftGrid.getData();
if(leftGridData.length == 0){
Swal.fire("검사할 항목이 없습니다.");
return;
}
// 우측 그리드 데이터 (불량상세) - 현재 선택된 품목의 불량상세
var rightGridData = rightGrid.getData();
Swal.fire({
title: '저장 확인',
text: '수입검사 내용을 저장하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '저장',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
var param = $("#form1").serializeObject();
param.dataListJson = JSON.stringify(leftGridData);
param.defectListJson = JSON.stringify(rightGridData);
param.selectedDetailObjid = selectedDetailObjid;
$.ajax({
url: "/quality/saveIncomingInspectionProgress.do",
type: "POST",
data: param,
dataType: "json",
success: function(data) {
Swal.fire({
title: data.result ? '저장 완료' : '저장 실패',
text: data.msg,
icon: data.result ? 'success' : 'error'
}).then(() => {
if(data.result){
// 우측 그리드 새로고침
if(selectedDetailObjid) {
fn_searchRightGrid(selectedDetailObjid);
}
// 부모창 새로고침
if(opener && opener.fn_search) {
opener.fn_search();
}
}
});
},
error: function(xhr, status, error) {
Swal.fire('서버 오류가 발생했습니다.');
}
});
}
});
}
// 팝업 닫힐 때 부모 새로고침
window.addEventListener('unload', function() {
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
});
</script>
<body style="overflow-x: hidden;">
<form name="form1" id="form1" action="" method="post" onsubmit="return false">
<input type="hidden" name="PURCHASE_ORDER_MASTER_OBJID" id="PURCHASE_ORDER_MASTER_OBJID" value="${param.OBJID}">
<input type="hidden" name="REQUEST_DATE" id="REQUEST_DATE" value="${info.REQUEST_DATE}">
<input type="hidden" name="REQUEST_USER_ID" id="REQUEST_USER_ID" value="${info.REQUEST_USER_ID}">
<div class="plm_menu_name_gdnsi">
<h2><span>수입검사 진행</span></h2>
<div class="btnArea">
<input type="button" value="저장" class="plm_btns" id="btnSave">
<input type="button" value="닫기" class="plm_btns" id="btnClose">
</div>
</div>
<!-- <div class="form-header">
<div>
<label>발주번호</label>
<span class="info-text">${info.PURCHASE_ORDER_NO}</span>
</div>
<div>
<label>요청일</label>
<span class="info-text">${info.REQUEST_DATE}</span>
</div>
<div>
<label>요청자</label>
<span class="info-text">${info.REQUEST_USER_NAME}</span>
</div>
<div style="border-left: 1px solid #ccc; padding-left: 20px;">
<label>검사일<span class="required">*</span></label>
<input type="text" name="INSPECTION_DATE" id="INSPECTION_DATE" class="date_icon" style="width:120px;" value="${info.INSPECTION_DATE}">
</div>
<div>
<label>검사자<span class="required">*</span></label>
<select name="INSPECTOR_ID" id="INSPECTOR_ID" class="select2" style="width:150px;">
<option value="">선택</option>
${code_map.inspector_id}
</select>
</div>
</div> -->
<!-- 좌/우 분할 레이아웃 -->
<div class="split-container">
<!-- 좌측: 입고품목 목록 -->
<div class="left-panel">
<div class="panel-header">
<span>입고품목</span>
</div>
<div class="panel-body">
<div id="leftGrid" class="grid-container"></div>
</div>
</div>
<!-- 우측: 불량상세 목록 -->
<div class="right-panel">
<div class="panel-header">
<span>불량상세</span>
<div class="btn-group">
<button type="button" id="btnAddRow" class="btn-add">+ 행 추가</button>
<button type="button" id="btnDeleteRow" class="btn-delete">- 행 삭제</button>
</div>
</div>
<div class="panel-body" id="rightGridContainer">
<div id="rightGrid" class="grid-container"></div>
</div>
</div>
</div>
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
</form>
</body>
</html>

View File

@@ -0,0 +1,265 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<%
PersonBean person = (PersonBean) session.getAttribute(Constants.PERSON_BEAN);
String connector = person.getUserId();
String connectorName = person.getUserName();
%>
<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>
</head>
<style>
</style>
<script type="text/javascript">
var _editable = true;
var grid;
// 요청자 목록
var _REQUEST_USER_LIST = [];
// 로그인 사용자 정보
var _LOGIN_USER_ID = "<%=connector%>";
var _LOGIN_USER_NAME = "<%=connectorName%>";
var _TODAY = "${today}";
$(document).ready(function(){
fnc_datepick();
$('.select2').select2();
// 요청자 목록 조회
_REQUEST_USER_LIST = [{"CODE": "", "NAME": "선택"}].concat(
fnc_getJsonAllDataListBySqlId({"sqlId": "common.getUserselect"})
);
// 저장 버튼
$("#btnSave").click(function(){
fn_save();
});
// 닫기 버튼
$("#btnClose").click(function(){
self.close();
});
// 초기화 버튼
$("#btnReset").click(function(){
fn_reset();
});
// 엑셀 다운로드 버튼
$("#btnExcel").click(function(){
fn_excelDownload();
});
// 그리드 초기화 및 조회
fn_search();
});
// 편집 가능 여부 체크
function editCheck(cell) {
return _editable;
}
// 품목상세 팝업
function fn_openPartDetailPopUp(partObjId) {
if (!partObjId) return;
var popup_width = 1200;
var popup_height = 900;
var url = "/partMng/partMngDetailPopUp.do?OBJID=" + partObjId;
window.open(url, "partMngDetailPopUp", "width=" + popup_width + ",height=" + popup_height + ",scrollbars=yes,resizable=yes");
}
function fn_search() {
var columns = [
{title:'품번', field:'PART_NO', headerHozAlign:'center', hozAlign:'left', width:300, editor:false,
formatter: fnc_createGridAnchorTag,
cellClick: function(e, cell) {
fn_openPartDetailPopUp(cell.getData().PART_OBJID);
}
},
{title:'품명', field:'PART_NAME', headerHozAlign:'center', hozAlign:'left', width:300, editor:false},
{title:'입고일', field:'DELIVERY_DATE', headerHozAlign:'center', hozAlign:'center', width:110, editor:false},
{title:'입고수량', field:'DELIVERY_QTY', headerHozAlign:'center', hozAlign:'right', width:110, editor:false,
formatter:"money", formatterParams:{thousand:",", precision:false}
},
{title:'검사여부', field:'INSPECTION_YN', headerHozAlign:'center', hozAlign:'center', width:110,
editor:"select",
editorParams:{values:{"검사":"검사", "스킵":"스킵"}}
},
{title:'요청일', field:'REQUEST_DATE', headerHozAlign:'center', hozAlign:'center', width:110,
editor:"input",
editable: editCheck
},
{title:'요청자', field:'REQUEST_USER_ID', headerHozAlign:'center', hozAlign:'center', width:110,
editor: fnc_customSelectEditor,
formatter: function(cell) {
return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_REQUEST_USER_LIST});
},
editorParams: {valueId:"CODE", labelId:"NAME", values:_REQUEST_USER_LIST},
editable: editCheck
}
];
var param = {
PURCHASE_ORDER_MASTER_OBJID: "${param.OBJID}"
};
// 그리드 조회
grid = fnc_tabul_search(_tabul_layout_fitDataStretch, grid, "/quality/incomingInspectionDetailGridList.do", columns, false, false, true, param);
// 데이터 로드 완료 후 기본값 설정
setTimeout(function(){
fn_setDefaultValues();
}, 300);
}
// 기본값 설정 (요청일: 오늘, 요청자: 로그인 사용자)
function fn_setDefaultValues() {
if(!grid) return;
var rows = grid.getRows();
for(var i = 0; i < rows.length; i++){
var row = rows[i];
var data = row.getData();
var updateObj = {};
var needUpdate = false;
// 요청일이 비어있으면 오늘 날짜로 설정
if(!data.REQUEST_DATE || data.REQUEST_DATE == '' || data.REQUEST_DATE == null){
updateObj.REQUEST_DATE = _TODAY;
needUpdate = true;
}
// 요청자가 비어있으면 로그인 사용자로 설정
if(!data.REQUEST_USER_ID || data.REQUEST_USER_ID == '' || data.REQUEST_USER_ID == null){
updateObj.REQUEST_USER_ID = _LOGIN_USER_ID;
needUpdate = true;
}
if(needUpdate){
row.update(updateObj);
}
}
}
// 초기화 - 모든 행에 오늘 날짜, 로그인 사용자 설정
function fn_reset() {
if(!grid) return;
var rows = grid.getRows();
for(var i = 0; i < rows.length; i++){
var row = rows[i];
row.update({
REQUEST_DATE: _TODAY,
REQUEST_USER_ID: _LOGIN_USER_ID
});
}
}
// 엑셀 다운로드
function fn_excelDownload() {
grid.download("xlsx", "수입검사요청_" + fnc_getToday() + ".xlsx");
}
// 저장
function fn_save() {
var gridData = grid.getData();
if(gridData.length == 0){
Swal.fire("요청할 항목이 없습니다.");
return;
}
// 유효성 검사: 검사여부가 선택된 항목은 요청일, 요청자 필수
var hasError = false;
for(var i = 0; i < gridData.length; i++){
var row = gridData[i];
if(row.INSPECTION_YN && row.INSPECTION_YN != ''){
if(!row.REQUEST_DATE || row.REQUEST_DATE == ''){
Swal.fire((i+1) + "번째 행의 요청일을 입력해주세요.");
hasError = true;
break;
}
if(!row.REQUEST_USER_ID || row.REQUEST_USER_ID == ''){
Swal.fire((i+1) + "번째 행의 요청자를 선택해주세요.");
hasError = true;
break;
}
}
}
if(hasError) return;
Swal.fire({
title: '저장 확인',
text: '수입검사 요청 내용을 저장하시겠습니까?',
icon: 'question',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: '저장',
cancelButtonText: '취소'
}).then((result) => {
if (result.isConfirmed) {
var param = $("#form1").serializeObject();
param.dataListJson = JSON.stringify(gridData);
$.ajax({
url: "/quality/saveIncomingInspection.do",
type: "POST",
data: param,
dataType: "json",
success: function(data) {
Swal.fire({
title: data.result ? '저장 완료' : '저장 실패',
text: data.msg,
icon: data.result ? 'success' : 'error'
}).then(() => {
if(data.result){
opener.fn_search();
self.close();
}
});
},
error: function(xhr, status, error) {
Swal.fire('서버 오류가 발생했습니다.');
}
});
}
});
}
// 팝업 닫힐 때 부모 새로고침
window.addEventListener('unload', function() {
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
}
});
</script>
<body style="overflow-x: hidden;">
<form name="form1" id="form1" action="" method="post" onsubmit="return false">
<input type="hidden" name="PURCHASE_ORDER_MASTER_OBJID" id="PURCHASE_ORDER_MASTER_OBJID" value="${param.OBJID}">
<div class="plm_menu_name_gdnsi">
<h2><span>수입검사 요청</span></h2>
<div class="btnArea">
<input type="button" value="저장" class="plm_btns" id="btnSave">
<input type="button" value="닫기" class="plm_btns" id="btnClose">
<input type="button" value="초기화" class="plm_btns" id="btnReset">
<input type="button" value="Excel Download" class="plm_btns excel" id="btnExcel">
</div>
</div>
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
</form>
</body>
</html>

View File

@@ -1324,6 +1324,17 @@ public class PurchaseOrderController {
return paramMap;
}
/**
* 매입마감 처리
* PURCHASE_ORDER_MASTER의 PURCHASE_CLOSE_DATE를 현재일자로 업데이트
*/
@ResponseBody
@RequestMapping("/purchaseOrder/purchaseClose.do")
public Map<String, Object> purchaseClose(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
String objIds = (String) paramMap.get("objIds");
return purchaseOrderService.updatePurchaseCloseDate(objIds);
}
/**
* 입고 관리
*/

View File

@@ -259,7 +259,7 @@ public class QualityController {
// =====================================================
/**
* 수입검사 관리 목록 화면
* 수입검사 요청 목록 화면
* @param request
* @param paramMap
* @return
@@ -269,6 +269,36 @@ public class QualityController {
ArrayList list = new ArrayList();
Map code_map = new HashMap();
try {
// 프로젝트번호
code_map.put("project_no", commonService.bizMakeOptionList("", (String)paramMap.get("project_no"), "common.getProjectNameList"));
// 공급업체 (CLIENT_MNG)
code_map.put("partner_objid", commonService.bizMakeOptionList("", (String)paramMap.get("partner_objid"), "common.getClientMngSupplySelect"));
// 요청자
code_map.put("request_user_id", commonService.bizMakeOptionList("", (String)paramMap.get("request_user_id"), "common.getUserselect"));
list = commonService.selectListPaging("quality.getIncomingInspectionList", request, paramMap);
} catch(Exception e) {
e.printStackTrace();
}
request.setAttribute("code_map", code_map);
request.setAttribute("LIST", JsonUtil.ListToJson(list));
return "/quality/incomingInspectionList";
}
/**
* 수입검사 진행 목록 화면
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/quality/incomingInspectionProgressList.do")
public String incomingInspectionProgressList(HttpServletRequest request, @RequestParam Map paramMap){
ArrayList list = new ArrayList();
Map code_map = new HashMap();
try {
// 프로젝트번호
code_map.put("project_no", commonService.bizMakeOptionList("", (String)paramMap.get("project_no"), "common.getProjectNameList"));
@@ -285,7 +315,7 @@ public class QualityController {
request.setAttribute("code_map", code_map);
request.setAttribute("LIST", JsonUtil.ListToJson(list));
return "/quality/incomingInspectionList";
return "/quality/incomingInspectionProgressList";
}
/**
@@ -302,7 +332,79 @@ public class QualityController {
}
/**
* 수입검사 등록 팝업
* 수입검사 요청 팝업
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/quality/incomingInspectionRequestPopUp.do")
public String incomingInspectionRequestPopUp(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map code_map = new HashMap();
try {
// 로그인 사용자 정보
PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN);
String loginUserId = person != null ? person.getUserId() : "";
// 기존 요청 정보 조회 (요청자, 요청일 가져오기)
Map info = service.getIncomingInspectionDetailInfo(paramMap);
String savedRequestUserId = "";
if(info != null && info.get("request_user_id") != null) {
savedRequestUserId = (String) info.get("request_user_id");
request.setAttribute("info", CommonUtils.toUpperCaseMapKey(info));
} else {
// 저장된 값 없으면 로그인 사용자로 기본 설정
savedRequestUserId = loginUserId;
}
// 요청자 목록 (기존 값 또는 로그인 사용자 selected)
code_map.put("request_user_id", commonService.bizMakeOptionList("", savedRequestUserId, "common.getUserselect"));
} catch(Exception e) {
e.printStackTrace();
}
request.setAttribute("code_map", code_map);
return "/quality/incomingInspectionRequestPopUp";
}
/**
* 수입검사 진행 팝업
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/quality/incomingInspectionProgressPopUp.do")
public String incomingInspectionProgressPopUp(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map code_map = new HashMap();
try {
// 로그인 사용자 정보
PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN);
String loginUserId = person != null ? person.getUserId() : "";
// 기존 검사 정보 조회 (검사자, 검사일 가져오기)
Map info = service.getIncomingInspectionDetailInfo(paramMap);
String savedInspectorId = "";
if(info != null && info.get("inspector_id") != null) {
savedInspectorId = (String) info.get("inspector_id");
request.setAttribute("info", CommonUtils.toUpperCaseMapKey(info));
} else {
// 저장된 값 없으면 로그인 사용자로 기본 설정
savedInspectorId = loginUserId;
}
// 검사자 목록 (기존 값 또는 로그인 사용자 selected)
code_map.put("inspector_id", commonService.bizMakeOptionList("", savedInspectorId, "common.getUserselect"));
} catch(Exception e) {
e.printStackTrace();
}
request.setAttribute("code_map", code_map);
return "/quality/incomingInspectionProgressPopUp";
}
/**
* 수입검사 등록 팝업 (기존 - 호환용)
* @param request
* @param paramMap
* @return
@@ -321,7 +423,7 @@ public class QualityController {
String savedInspectorId = "";
if(info != null && info.get("inspector_id") != null) {
savedInspectorId = (String) info.get("inspector_id");
request.setAttribute("info", info);
request.setAttribute("info", CommonUtils.toUpperCaseMapKey(info));
} else {
// 저장된 값 없으면 로그인 사용자로 기본 설정
savedInspectorId = loginUserId;
@@ -350,6 +452,32 @@ public class QualityController {
return paramMap;
}
/**
* 수입검사 진행 상세 그리드 목록 조회 (검사여부='검사'인 항목만)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping("/quality/incomingInspectionProgressDetailGridList.do")
public Map incomingInspectionProgressDetailGridList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
commonService.selectListPagingNew("quality.getIncomingInspectionProgressDetailList", request, paramMap);
return paramMap;
}
/**
* 수입검사 요청 저장
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/quality/saveIncomingInspectionRequest.do")
public String saveIncomingInspectionRequest(HttpServletRequest request, @RequestParam Map paramMap){
Map map = service.saveIncomingInspectionRequest(request, paramMap);
request.setAttribute("RESULT", CommonUtils.getJsonMap(map));
return "/ajax/ajaxResult";
}
/**
* 수입검사 저장
* @param request
@@ -687,4 +815,36 @@ public class QualityController {
return service.saveEcrResult(request, paramMap);
}
// =====================================================
// 수입검사 불량상세 관련
// =====================================================
/**
* 불량상세 목록 조회
*/
@ResponseBody
@RequestMapping("/quality/getIncomingInspectionDefectList.do")
public Map getIncomingInspectionDefectList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map result = new HashMap();
try {
List list = service.getIncomingInspectionDefectList(paramMap);
result.put("list", list);
result.put("result", true);
} catch(Exception e) {
e.printStackTrace();
result.put("result", false);
result.put("msg", e.getMessage());
}
return result;
}
/**
* 수입검사 진행 저장 (입고품목 + 불량상세)
*/
@ResponseBody
@RequestMapping("/quality/saveIncomingInspectionProgress.do")
public Map saveIncomingInspectionProgress(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
return service.saveIncomingInspectionProgress(request, paramMap);
}
}

View File

@@ -1115,6 +1115,14 @@
WHERE OBJID = (SELECT PURCHASE_ORDER_MASTER_OBJID FROM PURCHASE_ORDER_PART POP WHERE OBJID = #{PURCHASE_ORDER_PART_OBJID })
-->
</update>
<!-- 매입마감 처리: PURCHASE_CLOSE_DATE를 현재일자로 업데이트 -->
<update id="updatePurchaseCloseDate" parameterType="map">
UPDATE PURCHASE_ORDER_MASTER
SET PURCHASE_CLOSE_DATE = TO_CHAR(NOW(), 'YYYY-MM-DD')
WHERE OBJID = #{OBJID}
</update>
<update id="updatePurchaseOrderMasterPriceAll" parameterType="map">
UPDATE PURCHASE_ORDER_MASTER SET
TOTAL_PRICE_TXT_ALL = NUM_TO_KOR((T.REAL_SUPPLY_PRICE_VAT)::varchar,'일금 ',' 원정 (₩ ') || TRIM(TO_CHAR((T.REAL_SUPPLY_PRICE_VAT), '999,999,999,999')) ||')'
@@ -4273,6 +4281,7 @@ SELECT POM.OBJID
,POM.TOTAL_PRICE
,POM.DISCOUNT_PRICE
,POM.TOTAL_SUPPLY_UNIT_PRICE
,POM.TOTAL_SUPPLY_PRICE
,POM.NEGO_RATE
,POM.MULTI_MASTER_YN
,POM.MULTI_YN
@@ -4282,6 +4291,7 @@ SELECT POM.OBJID
<!--,(SELECT SUM(REAL_ORDER_QTY::NUMERIC) FROM PURCHASE_ORDER_PART AS O WHERE POM.OBJID::VARCHAR = O.PURCHASE_ORDER_MASTER_OBJID) AS TOTAL_PO_QTY -->
,S1.CUR_DELIVERY_DATE
,S1.TOTAL_DELIVERY_QTY
,S1.TOTAL_DELIVERY_PRICE
<!-- ,(S1.TOTAL_PO_QTY - S1.TOTAL_DELIVERY_QTY - S1.TOTAL_DEFECT_QTY) AS NON_DELIVERY_QTY -->
,((SELECT SUM(ORDER_QTY::NUMERIC) FROM PURCHASE_ORDER_PART AS O WHERE POM.OBJID::VARCHAR = O.PURCHASE_ORDER_MASTER_OBJID) - (S1.TOTAL_DELIVERY_QTY <!-- - S1.TOTAL_DEFECT_QTY --> )) AS NON_DELIVERY_QTY
<!-- ,S1.TOTAL_DEFECT_QTY -->
@@ -4299,6 +4309,7 @@ SELECT POM.OBJID
,(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = POM.ORDER_TYPE_CD) AS ORDER_TYPE_CD_NAME
,(SELECT user_name(RECEIVER_ID) FROM ARRIVAL_PLAN AP WHERE AP.parent_objid = POM.OBJID ORDER BY AP.RECEIPT_DATE desc LIMIT 1) AS CUR_RECEIVER_NAME
,(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
,POM.PURCHASE_CLOSE_DATE
<!-- ,A.APPR_STATUS_NAME -->
FROM PURCHASE_ORDER_MASTER AS POM
<!-- LEFT OUTER JOIN(
@@ -4334,6 +4345,8 @@ SELECT POM.OBJID
,SUM(POP.ORDER_QTY::NUMERIC) AS TOTAL_PO_QTY
,MAX(DH.RECEIPT_DATE) AS CUR_DELIVERY_DATE
,SUM(DH.RECEIPT_QTY::NUMERIC) AS TOTAL_DELIVERY_QTY
,SUM(pop.partner_price::NUMERIC * DH.RECEIPT_QTY::NUMERIC) AS TOTAL_DELIVERY_PRICE
<!-- ,SUM(DH.ERROR_QTY::NUMERIC) AS TOTAL_DEFECT_QTY -->
<!-- ,MAX(DH.DELIVERY_DATE) AS CUR_DELIVERY_DATE
,SUM(DH.DELIVERY_QTY::NUMERIC) AS TOTAL_DELIVERY_QTY

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>
@@ -1643,7 +1709,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
@@ -1666,6 +1735,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>
@@ -1685,6 +1802,8 @@
OBJID
, INVENTORY_IN_OBJID
, PURCHASE_ORDER_MASTER_OBJID
, REQUEST_DATE
, REQUEST_USER_ID
, INSPECTION_DATE
, INSPECTOR_ID
, INSPECTION_TYPE
@@ -1703,6 +1822,8 @@
#{NEW_OBJID}
, #{OBJID}
, #{PURCHASE_ORDER_MASTER_OBJID}
, #{REQUEST_DATE}
, #{REQUEST_USER_ID}
, #{INSPECTION_DATE}
, #{INSPECTOR_ID}
, #{INSPECTION_TYPE}
@@ -1719,20 +1840,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

@@ -3134,4 +3134,36 @@ public class PurchaseOrderService {
return sb.toString();
}
/**
* 매입마감 처리
* PURCHASE_ORDER_MASTER의 PURCHASE_CLOSE_DATE를 현재일자로 업데이트
* @param objIds 쉼표로 구분된 OBJID 목록
* @return 처리 결과
*/
public Map<String, Object> updatePurchaseCloseDate(String objIds) {
Map<String, Object> resultMap = new HashMap<String, Object>();
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
try {
if(objIds != null && !objIds.isEmpty()) {
String[] objIdArr = objIds.split(",");
for(String objId : objIdArr) {
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.put("OBJID", objId.trim());
sqlSession.update("purchaseOrder.updatePurchaseCloseDate", updateParam);
}
}
sqlSession.commit();
resultMap.put("result", "SUCCESS");
} catch(Exception e) {
sqlSession.rollback();
e.printStackTrace();
resultMap.put("result", "FAIL");
resultMap.put("message", e.getMessage());
} finally {
sqlSession.close();
}
return resultMap;
}
}

View File

@@ -17,6 +17,7 @@ import com.pms.common.bean.PersonBean;
import com.pms.common.service.BaseService;
import com.pms.common.utils.CommonUtils;
import com.pms.common.utils.Constants;
import com.pms.common.JsonUtil;
@Service
public class QualityService extends BaseService{
@@ -661,8 +662,60 @@ public class QualityService extends BaseService{
return resultMap;
}
/**
* 수입검사 요청 저장 (검사여부, 요청일, 요청자만 저장)
*/
public Map saveIncomingInspectionRequest(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
String writer = CommonUtils.checkNull(person.getUserId());
String requestDate = CommonUtils.checkNull(paramMap.get("REQUEST_DATE"));
String requestUserId = CommonUtils.checkNull(paramMap.get("REQUEST_USER_ID"));
String purchaseOrderMasterObjId = CommonUtils.checkNull(paramMap.get("PURCHASE_ORDER_MASTER_OBJID"));
String dataListJson = CommonUtils.checkNull(paramMap.get("dataListJson"));
// JSON 파싱
com.google.gson.Gson gson = new com.google.gson.Gson();
java.lang.reflect.Type listType = new com.google.gson.reflect.TypeToken<List<Map<String, String>>>(){}.getType();
List<Map<String, String>> dataList = gson.fromJson(dataListJson, listType);
// 각 행별로 저장 (요청 정보만)
for(Map<String, String> data : dataList){
Map sqlParamMap = new HashMap();
sqlParamMap.put("NEW_OBJID", CommonUtils.createObjId()); // 신규 OBJID 생성
sqlParamMap.put("OBJID", data.get("OBJID")); // INVENTORY_MGMT_IN.OBJID
sqlParamMap.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjId);
sqlParamMap.put("REQUEST_DATE", requestDate);
sqlParamMap.put("REQUEST_USER_ID", requestUserId);
sqlParamMap.put("INSPECTION_YN", data.get("INSPECTION_YN"));
sqlParamMap.put("WRITER", writer);
sqlSession.update("quality.saveIncomingInspectionDetail", sqlParamMap);
}
sqlSession.commit();
resultMap.put("result", true);
resultMap.put("msg", Message.SAVE_SUCCESS);
}catch(Exception e){
resultMap.put("result", false);
resultMap.put("msg", Message.SAVE_FAILED);
if(sqlSession != null) sqlSession.rollback();
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
/**
* 수입검사 저장 (그리드 데이터 일괄 저장)
* - 요청일/요청자: 각 행별로 개별 저장
* - 검사일/검사자: 헤더에서 공통 적용 (진행 팝업에서 사용)
*/
public Map saveIncomingInspection(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
@@ -673,6 +726,7 @@ public class QualityService extends BaseService{
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
String writer = CommonUtils.checkNull(person.getUserId());
// 헤더에서 가져오는 값 (진행 팝업용)
String inspectionDate = CommonUtils.checkNull(paramMap.get("INSPECTION_DATE"));
String inspectorId = CommonUtils.checkNull(paramMap.get("INSPECTOR_ID"));
String purchaseOrderMasterObjId = CommonUtils.checkNull(paramMap.get("PURCHASE_ORDER_MASTER_OBJID"));
@@ -689,8 +743,15 @@ public class QualityService extends BaseService{
sqlParamMap.put("NEW_OBJID", CommonUtils.createObjId()); // 신규 OBJID 생성
sqlParamMap.put("OBJID", data.get("OBJID")); // INVENTORY_MGMT_IN.OBJID
sqlParamMap.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjId);
// 검사일/검사자: 헤더 값 사용 (진행 팝업)
sqlParamMap.put("INSPECTION_DATE", inspectionDate);
sqlParamMap.put("INSPECTOR_ID", inspectorId);
// 요청일/요청자: 각 행별 개별 값 사용 (요청 팝업)
sqlParamMap.put("REQUEST_DATE", data.get("REQUEST_DATE"));
sqlParamMap.put("REQUEST_USER_ID", data.get("REQUEST_USER_ID"));
sqlParamMap.put("INSPECTION_TYPE", data.get("INSPECTION_TYPE"));
sqlParamMap.put("INSPECTION_YN", data.get("INSPECTION_YN"));
sqlParamMap.put("DEFECT_TYPE", data.get("DEFECT_TYPE"));
@@ -1152,4 +1213,117 @@ public class QualityService extends BaseService{
/*edhwang end*/
// =====================================================
// 수입검사 불량상세 관련
// =====================================================
/**
* 불량상세 목록 조회
*/
public List getIncomingInspectionDefectList(Map paramMap){
SqlSession sqlSession = null;
List resultList = new ArrayList();
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession();
resultList = sqlSession.selectList("quality.getIncomingInspectionDefectList", paramMap);
resultList = CommonUtils.keyChangeUpperList(resultList);
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultList;
}
/**
* 수입검사 진행 저장 (입고품목 + 불량상세)
*/
public Map saveIncomingInspectionProgress(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession();
// 작성자 정보 설정
PersonBean person = (PersonBean) request.getSession().getAttribute(Constants.PERSON_BEAN);
String writer = person != null ? person.getUserId() : "";
String purchaseOrderMasterObjid = CommonUtils.checkNull(paramMap.get("PURCHASE_ORDER_MASTER_OBJID"));
String inspectionDate = CommonUtils.checkNull(paramMap.get("INSPECTION_DATE"));
String inspectorId = CommonUtils.checkNull(paramMap.get("INSPECTOR_ID"));
// 1. 입고품목 데이터 저장 (검사구분 등)
String dataListJson = CommonUtils.checkNull(paramMap.get("dataListJson"));
if(!dataListJson.isEmpty()) {
List<Map<String, Object>> dataList = JsonUtil.JsonToList(dataListJson);
for(Map data : dataList) {
Map sqlParamMap = new HashMap();
sqlParamMap.put("NEW_OBJID", CommonUtils.createObjId()); // 신규 OBJID 생성
sqlParamMap.put("OBJID", CommonUtils.checkNull(data.get("OBJID"))); // INVENTORY_MGMT_IN.OBJID
sqlParamMap.put("PURCHASE_ORDER_MASTER_OBJID", purchaseOrderMasterObjid);
sqlParamMap.put("INSPECTION_TYPE", CommonUtils.checkNull(data.get("INSPECTION_TYPE")));
sqlParamMap.put("INSPECTION_DATE", inspectionDate);
sqlParamMap.put("INSPECTOR_ID", inspectorId);
sqlParamMap.put("WRITER", writer);
sqlSession.update("quality.saveIncomingInspectionDetail", sqlParamMap);
}
}
// 2. 불량상세 데이터 저장
String defectListJson = CommonUtils.checkNull(paramMap.get("defectListJson"));
String selectedDetailObjid = CommonUtils.checkNull(paramMap.get("selectedDetailObjid"));
if(!defectListJson.isEmpty() && !selectedDetailObjid.isEmpty()) {
List<Map<String, Object>> defectList = JsonUtil.JsonToList(defectListJson);
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);
sqlParamMap.put("INSPECTION_TYPE", CommonUtils.checkNull(defect.get("INSPECTION_TYPE")));
sqlParamMap.put("INSPECTION_DATE", CommonUtils.checkNull(defect.get("INSPECTION_DATE")));
sqlParamMap.put("INSPECTOR_ID", CommonUtils.checkNull(defect.get("INSPECTOR_ID")));
sqlParamMap.put("DEFECT_TYPE", CommonUtils.checkNull(defect.get("DEFECT_TYPE")));
sqlParamMap.put("DEFECT_REASON", CommonUtils.checkNull(defect.get("DEFECT_REASON")));
sqlParamMap.put("ACTION_STATUS", CommonUtils.checkNull(defect.get("ACTION_STATUS")));
sqlParamMap.put("ACTION_RESULT", CommonUtils.checkNull(defect.get("ACTION_RESULT")));
sqlParamMap.put("INSPECTION_QTY", CommonUtils.checkNull(defect.get("INSPECTION_QTY")));
sqlParamMap.put("DEFECT_QTY", CommonUtils.checkNull(defect.get("DEFECT_QTY")));
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);
} else {
// 신규/수정
sqlSession.insert("quality.saveIncomingInspectionDefect", sqlParamMap);
}
}
}
sqlSession.commit();
resultMap.put("result", true);
resultMap.put("msg", "저장되었습니다.");
}catch(Exception e){
if(sqlSession != null) sqlSession.rollback();
resultMap.put("result", false);
resultMap.put("msg", e.getMessage());
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
}