생산계획&실적관리(장비) 메뉴 추가, wbs 할당 기능 추가
This commit is contained in:
@@ -0,0 +1,587 @@
|
||||
<%@ 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>
|
||||
<%
|
||||
// DB에서 메뉴명 조회 (공통 유틸 사용)
|
||||
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>
|
||||
<style>
|
||||
body, html {
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.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;
|
||||
}
|
||||
/* WBS할당 모달 */
|
||||
.wbs-modal-overlay {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background: rgba(0,0,0,0.4);
|
||||
z-index: 9998;
|
||||
}
|
||||
.wbs-modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 9999;
|
||||
background: #fff;
|
||||
border: 2px solid #FFA500;
|
||||
border-radius: 4px;
|
||||
width: 420px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||
}
|
||||
.wbs-modal-header {
|
||||
background-color: #FFA500;
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
padding: 8px 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.wbs-modal-body {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
.wbs-modal-body .wbs-desc {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.wbs-modal-body table td {
|
||||
padding: 5px 5px;
|
||||
}
|
||||
.wbs-modal-body select {
|
||||
width: 220px;
|
||||
}
|
||||
.wbs-modal-footer {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
// Tabulator 그리드 전역 변수
|
||||
var _tabulGrid;
|
||||
|
||||
$(document).ready(function(){
|
||||
|
||||
_fnc_datepick();
|
||||
$('.select2').select2();
|
||||
|
||||
// 품번/품명 Select2 AJAX 초기화
|
||||
initPartSelect2Ajax("#search_part_no", "#search_part_name", "#search_part_objid");
|
||||
|
||||
// Enter 키로 검색
|
||||
$("#plmSearchZon input").keyup(function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
fn_search();
|
||||
}
|
||||
});
|
||||
|
||||
// 조회 버튼
|
||||
$("#btnSearch").click(function(){
|
||||
fn_search();
|
||||
});
|
||||
|
||||
// WBS할당 버튼
|
||||
$("#btnWbsAssign").click(function(){
|
||||
fn_toggleWbsAssign();
|
||||
});
|
||||
|
||||
|
||||
// 초기 조회
|
||||
fn_search();
|
||||
});
|
||||
|
||||
// 날짜 선택기 초기화 함수
|
||||
function _fnc_datepick(){
|
||||
var $dateinput = $("input.date_icon");
|
||||
for(var i=0; i<$dateinput.length; i++){
|
||||
$dateinput.eq(i).attr("size","10");
|
||||
$dateinput.eq(i).datepicker({
|
||||
changeMonth:true,
|
||||
changeYear:true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 그리드 컬럼 정의
|
||||
var columns = [
|
||||
{title:'OBJID', field:'OBJID', visible: false, frozen: true},
|
||||
|
||||
// 프로젝트번호
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 130,
|
||||
title: '프로젝트번호',
|
||||
field: 'PROJECT_NO',
|
||||
frozen: true,
|
||||
formatter: fnc_createGridAnchorTag,
|
||||
cellClick: function(e, cell){
|
||||
var orderNo = cell.getData().PROJECT_NO;
|
||||
fn_openSaleRegPopup(orderNo, "detail");
|
||||
}
|
||||
},
|
||||
|
||||
// 제품구분
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: '제품구분',
|
||||
field: 'PRODUCT_NAME'
|
||||
},
|
||||
|
||||
// 주문유형
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: '주문유형',
|
||||
field: 'CATEGORY_CODE_NAME'
|
||||
},
|
||||
|
||||
// 생산유형
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: '생산유형',
|
||||
field: 'PRODUCTION_TYPE_NAME'
|
||||
},
|
||||
|
||||
// 고객사
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '고객사',
|
||||
field: 'CUSTOMER_NAME'
|
||||
},
|
||||
|
||||
// 요청납기
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 90,
|
||||
title: '요청납기',
|
||||
field: 'REQ_DEL_DATE'
|
||||
},
|
||||
|
||||
// 고객사요청사항
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 230,
|
||||
title: '고객사요청사항',
|
||||
field: 'CUSTOMER_REQUEST'
|
||||
},
|
||||
|
||||
// 품번
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 150,
|
||||
title: '품번',
|
||||
field: 'PART_NO'
|
||||
},
|
||||
|
||||
// 품명
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'left',
|
||||
width: 180,
|
||||
title: '품명',
|
||||
field: 'PART_NAME'
|
||||
},
|
||||
|
||||
// S/N
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 80,
|
||||
title: 'S/N',
|
||||
field: 'SERIAL_NO'
|
||||
},
|
||||
|
||||
// 생산WBS
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '생산WBS',
|
||||
field: 'PROD_WBS_CNT',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick: function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
var cnt = cell.getValue();
|
||||
if(!objid) return;
|
||||
if(!cnt || cnt == 0) { Swal.fire({title:'알림', text:'WBS할당을 먼저 진행해주세요.', icon:'info'}); return; }
|
||||
wbs_popup(objid, 'PRODUCE');
|
||||
}
|
||||
},
|
||||
|
||||
// 생산진척율(%)
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 110,
|
||||
title: '생산진척율(%)',
|
||||
field: 'PROD_PROGRESS_RATE',
|
||||
formatter: function(cell) {
|
||||
var val = cell.getValue();
|
||||
if(val == null || val === '') return '';
|
||||
return val + '%';
|
||||
}
|
||||
},
|
||||
|
||||
// 납품WBS
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'center',
|
||||
width: 100,
|
||||
title: '납품WBS',
|
||||
field: 'DELV_WBS_CNT',
|
||||
formatter: fnc_subInfoValueFormatter,
|
||||
cellClick: function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
var cnt = cell.getValue();
|
||||
if(!objid) return;
|
||||
if(!cnt || cnt == 0) { Swal.fire({title:'알림', text:'WBS할당을 먼저 진행해주세요.', icon:'info'}); return; }
|
||||
wbs_popup(objid, 'SHIP');
|
||||
}
|
||||
},
|
||||
|
||||
// 납품진척율(%)
|
||||
{
|
||||
headerHozAlign: 'center',
|
||||
hozAlign: 'right',
|
||||
width: 110,
|
||||
title: '납품진척율(%)',
|
||||
field: 'DELV_PROGRESS_RATE',
|
||||
formatter: function(cell) {
|
||||
var val = cell.getValue();
|
||||
if(val == null || val === '') return '';
|
||||
return val + '%';
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
// 검색 함수
|
||||
function fn_search(){
|
||||
var projectNos = $("#search_project_no").val();
|
||||
if(projectNos && projectNos.length > 0) {
|
||||
$("#search_project_nos_hidden").val(projectNos.join(","));
|
||||
} else {
|
||||
$("#search_project_nos_hidden").val("");
|
||||
}
|
||||
|
||||
_tabulGrid = fnc_tabul_search(_tabul_layout_fitDataStretch, _tabulGrid, "/productionplanning/prodPlanResultMgmtEquipGridList.do", columns, true);
|
||||
}
|
||||
|
||||
// WBS할당 모달 열기
|
||||
function fn_toggleWbsAssign() {
|
||||
var checkedRows = getCheckedRows();
|
||||
if(checkedRows.length === 0) {
|
||||
Swal.fire({title: '선택 필요', text: 'WBS를 할당할 프로젝트를 선택해주세요.', icon: 'warning'});
|
||||
return;
|
||||
}
|
||||
if(checkedRows.length > 1) {
|
||||
Swal.fire({title: '알림', text: '한 번에 하나의 프로젝트만 선택해주세요.', icon: 'info'});
|
||||
return;
|
||||
}
|
||||
var row = checkedRows[0];
|
||||
var hasProd = row.PROD_WBS_CNT && row.PROD_WBS_CNT > 0;
|
||||
var hasDelv = row.DELV_WBS_CNT && row.DELV_WBS_CNT > 0;
|
||||
|
||||
if(hasProd || hasDelv) {
|
||||
var msg = '이미 WBS가 할당되어 있습니다.\n재할당 시 기존 데이터(담당자, 일정, 진척율 등)가 모두 삭제됩니다.\n계속하시겠습니까?';
|
||||
Swal.fire({title:'재할당 경고', text: msg, icon:'warning', showCancelButton:true, confirmButtonText:'재할당', cancelButtonText:'취소'
|
||||
}).then(function(result){
|
||||
if(result.isConfirmed) fn_openWbsModal(row.OBJID);
|
||||
});
|
||||
} else {
|
||||
fn_openWbsModal(row.OBJID);
|
||||
}
|
||||
}
|
||||
|
||||
function fn_openWbsModal(objid) {
|
||||
$("#wbsModal").data("projectObjid", objid);
|
||||
$("#wbs_produce").val("");
|
||||
$("#wbs_delivery").val("");
|
||||
$("#wbsModalOverlay").fadeIn(200);
|
||||
$("#wbsModal").fadeIn(200);
|
||||
}
|
||||
|
||||
// WBS할당 모달 닫기
|
||||
function fn_closeWbsModal() {
|
||||
$("#wbsModal").fadeOut(200);
|
||||
$("#wbsModalOverlay").fadeOut(200);
|
||||
}
|
||||
|
||||
// WBS할당 저장 (템플릿 할당)
|
||||
function fn_saveWbsAssign() {
|
||||
var projectObjid = $("#wbsModal").data("projectObjid");
|
||||
var prodTemplate = $("#wbs_produce").val();
|
||||
var delvTemplate = $("#wbs_delivery").val();
|
||||
|
||||
if(!prodTemplate && !delvTemplate) {
|
||||
Swal.fire({title: '알림', text: '생산WBS 또는 납품WBS를 선택해주세요.', icon: 'warning'});
|
||||
return;
|
||||
}
|
||||
|
||||
var requests = [];
|
||||
if(prodTemplate) {
|
||||
requests.push({ projectObjid: projectObjid, wbsType: 'PRODUCE', templateObjid: prodTemplate });
|
||||
}
|
||||
if(delvTemplate) {
|
||||
requests.push({ projectObjid: projectObjid, wbsType: 'SHIP', templateObjid: delvTemplate });
|
||||
}
|
||||
|
||||
var completed = 0;
|
||||
var hasError = false;
|
||||
for(var i=0; i<requests.length; i++) {
|
||||
(function(req){
|
||||
// 템플릿 태스크 조회 후 저장
|
||||
$.ajax({
|
||||
url: "/productionplanning/getWbsTemplateTasks.do",
|
||||
type: "POST", data: { templateObjid: req.templateObjid },
|
||||
dataType: "json",
|
||||
success: function(result){
|
||||
if(result.list && result.list.length > 0){
|
||||
var tasks = [];
|
||||
for(var j=0; j<result.list.length; j++){
|
||||
var d = result.list[j];
|
||||
tasks.push({
|
||||
objid: '', taskName: d.TASK_NAME || d.task_name || '',
|
||||
taskSeq: j+1, taskLevel: d.TASK_LEVEL || d.task_level || '',
|
||||
unitNo: d.UNIT_NO || d.unit_no || '',
|
||||
upperTaskObjid: d.UPPER_TASK_OBJID || d.upper_task_objid || '',
|
||||
templateTaskObjid: d.OBJID || d.objid || '',
|
||||
userId: '', planStart: '', planEnd: '', actStart: '', actEnd: '', remark: ''
|
||||
});
|
||||
}
|
||||
$.ajax({
|
||||
url: "/productionplanning/saveEquipWbsAssign.do",
|
||||
type: "POST", contentType: "application/json",
|
||||
data: JSON.stringify({ projectObjid: req.projectObjid, wbsType: req.wbsType, templateObjid: req.templateObjid, tasks: tasks }),
|
||||
dataType: "json",
|
||||
success: function(res){
|
||||
if(!res.success) hasError = true;
|
||||
completed++;
|
||||
if(completed === requests.length) fn_wbsAssignDone(hasError);
|
||||
},
|
||||
error: function(){ hasError = true; completed++; if(completed === requests.length) fn_wbsAssignDone(hasError); }
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
})(requests[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function fn_wbsAssignDone(hasError) {
|
||||
fn_closeWbsModal();
|
||||
if(hasError) {
|
||||
Swal.fire({title:'오류', text:'일부 할당에 실패했습니다.', icon:'error'});
|
||||
} else {
|
||||
Swal.fire({title:'완료', text:'WBS 할당이 완료되었습니다.', icon:'success'});
|
||||
}
|
||||
fn_search();
|
||||
}
|
||||
|
||||
// 폴더 아이콘 클릭 → 상세 편집 팝업
|
||||
function wbs_popup(objId, wbsType){
|
||||
var popup_width = 1200;
|
||||
var popup_height = 600;
|
||||
var url = "/productionplanning/prodPlanWbsAssignPopup.do?projectObjid=" + objId + "&wbsType=" + wbsType;
|
||||
fn_centerPopup(popup_width, popup_height, url, 'wbsAssignPopup');
|
||||
}
|
||||
|
||||
// 프로젝트 상세 팝업
|
||||
function fn_openProjectDetailPopup(objid) {
|
||||
var popup_width = 1000;
|
||||
var popup_height = 550;
|
||||
var url = "/salesMgmt/salesRegForm.do?orderNo=&saleNo=detail&objid=" + objid;
|
||||
fn_centerPopup(popup_width, popup_height, url);
|
||||
}
|
||||
|
||||
// 선택된 행 가져오기
|
||||
function getCheckedRows() {
|
||||
if(_tabulGrid) {
|
||||
return _tabulGrid.getSelectedData();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- hiddenForm -->
|
||||
<form name="hiddenForm" id="hiddenForm" method="post">
|
||||
<input type="hidden" name="OBJID" id="OBJID">
|
||||
<input type="hidden" name="actionType" id="actionType">
|
||||
</form>
|
||||
|
||||
<form name="form1" id="form1" method="post">
|
||||
<input type="hidden" name="actionType" id="actionType">
|
||||
<input type="hidden" name="search_project_nos" id="search_project_nos_hidden">
|
||||
<div class="content-box">
|
||||
<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="btnSearch">
|
||||
<input type="button" class="plm_btns" value="WBS할당" id="btnWbsAssign">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 검색 영역 -->
|
||||
<div id="plmSearchZon">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label>프로젝트번호</label></td>
|
||||
<td>
|
||||
<select name="search_project_no" id="search_project_no" class="select2" style="width:235px;" multiple="multiple">
|
||||
<option value="">선택</option>
|
||||
${code_map.project_no}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td><label>제품구분</label></td>
|
||||
<td>
|
||||
<select name="search_product_code" id="search_product_code" class="select2" style="">
|
||||
<option value="">전체</option>
|
||||
${code_map.product_cd}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td><label>주문유형</label></td>
|
||||
<td>
|
||||
<select name="search_category_code" id="search_category_code" class="select2" style="">
|
||||
<option value="">전체</option>
|
||||
${code_map.category_cd}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td><label>생산유형</label></td>
|
||||
<td>
|
||||
<select name="search_production_type" id="search_production_type" class="select2" style="">
|
||||
<option value="">전체</option>
|
||||
${code_map.production_type_cd}
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td><label>고객사</label></td>
|
||||
<td>
|
||||
<select name="search_customer_objid" id="search_customer_objid" class="select2" style="width:250px;">
|
||||
<option value="">전체</option>
|
||||
${code_map.customer_cd}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>요청납기</label></td>
|
||||
<td>
|
||||
<input type="text" name="search_req_del_date_from" id="search_req_del_date_from" class="date_icon" style="width:110px;" autocomplete="off">~
|
||||
<input type="text" name="search_req_del_date_to" id="search_req_del_date_to" class="date_icon" style="width:110px;" autocomplete="off">
|
||||
</td>
|
||||
|
||||
<td><label>품번</label></td>
|
||||
<td>
|
||||
<select name="search_part_no" id="search_part_no" class="select2-part" style="width:130px;">
|
||||
<option value="">품번 선택</option>
|
||||
</select>
|
||||
<input type="hidden" name="search_part_objid" id="search_part_objid" value="">
|
||||
</td>
|
||||
|
||||
<td><label>품명</label></td>
|
||||
<td>
|
||||
<select name="search_part_name" id="search_part_name" class="select2-part" style="width:130px;">
|
||||
<option value="">품명 선택</option>
|
||||
</select>
|
||||
</td>
|
||||
|
||||
<td><label>S/N</label></td>
|
||||
<td><input type="text" name="search_serial_no" id="search_serial_no" style="" autocomplete="off"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 그리드 영역 -->
|
||||
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- WBS할당 모달 -->
|
||||
<div id="wbsModalOverlay" class="wbs-modal-overlay" onclick="fn_closeWbsModal()"></div>
|
||||
<div id="wbsModal" class="wbs-modal">
|
||||
<div class="wbs-modal-header">WBS할당</div>
|
||||
<div class="wbs-modal-body">
|
||||
<div class="wbs-desc">└ 선택후 WBS 할당 버튼 클릭하여 WBS 할당</div>
|
||||
<table>
|
||||
<tr>
|
||||
<td>생산WBS</td>
|
||||
<td>
|
||||
<select id="wbs_produce">
|
||||
<option value="">선택</option>
|
||||
${code_map.wbs_template}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>납품WBS</td>
|
||||
<td>
|
||||
<select id="wbs_delivery">
|
||||
<option value="">선택</option>
|
||||
${code_map.wbs_template}
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="wbs-modal-footer">
|
||||
<input type="button" class="plm_btns" value="저장" onclick="fn_saveWbsAssign()">
|
||||
<input type="button" class="plm_btns" value="닫기" onclick="fn_closeWbsModal()">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,680 @@
|
||||
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
||||
<%@ 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"%>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<title><%=Constants.SYSTEM_NAME%></title>
|
||||
<style>
|
||||
html, body { height: 100%; margin: 0; overflow: hidden; }
|
||||
.info-area { padding: 8px 10px; }
|
||||
.info-area table td { padding: 2px 8px; }
|
||||
.info-area label { font-weight: bold; }
|
||||
#wbsTaskList td { text-align: center; padding: 2px 3px; vertical-align: middle; }
|
||||
#wbsTaskList input[type="text"] { width: 95%; font-size: 11px; }
|
||||
#wbsTaskList select { font-size: 11px; }
|
||||
#wbsTaskList input.date_input { width: 85px; font-size: 11px; text-align: center; }
|
||||
.row-total td { font-weight: bold; background-color: #FFF9C4 !important; }
|
||||
.row-level1 td { font-weight: bold; background-color: #FFF3E0 !important; }
|
||||
.row-level2 td { background-color: #F3E5F5 !important; }
|
||||
.calc-field { background: transparent; border: none; text-align: center; font-size: 11px; }
|
||||
.delay-plus { color: red; font-weight: bold; }
|
||||
.delay-minus { color: blue; }
|
||||
/* select2 컴팩트 스타일 */
|
||||
#wbsTaskList .select2-container { width: 100% !important; }
|
||||
#wbsTaskList .select2-container .select2-selection--single { height: 24px; min-height: 24px; }
|
||||
#wbsTaskList .select2-container .select2-selection--single .select2-selection__rendered { line-height: 24px; font-size: 11px; padding-left: 4px; }
|
||||
#wbsTaskList .select2-container .select2-selection--single .select2-selection__arrow { height: 22px; }
|
||||
</style>
|
||||
<script>
|
||||
var rowSeq = 0;
|
||||
var projectObjid = "${param.projectObjid}";
|
||||
var wbsType = "${param.wbsType}";
|
||||
var wbsTypeLabel = (wbsType === 'PRODUCE') ? '생산' : '납품';
|
||||
|
||||
// 담당자 옵션 HTML (서버에서 생성)
|
||||
var userOptionsHtml = '<option value=""></option>';
|
||||
<c:forEach var="user" items="${userList}">
|
||||
userOptionsHtml += '<option value="${user.CODE_ID}">${user.CODE_NAME}</option>';
|
||||
</c:forEach>
|
||||
|
||||
$(document).ready(function(){
|
||||
document.getElementById('wbsTypeLabel').innerText = wbsTypeLabel + ' WBS';
|
||||
|
||||
fn_loadExistingWbs();
|
||||
|
||||
$("#btnSave").click(function(){ fn_save(); });
|
||||
$("#btnClose").click(function(){ window.close(); });
|
||||
$("#wbsTemplateSelect").change(function(){
|
||||
if($(this).val()) fn_loadTemplateTasks($(this).val());
|
||||
});
|
||||
});
|
||||
|
||||
function fn_loadExistingWbs(){
|
||||
$.ajax({
|
||||
url: "/productionplanning/getEquipWbsTaskList.do",
|
||||
type: "POST", data: { projectObjid: projectObjid, wbsType: wbsType },
|
||||
dataType: "json",
|
||||
success: function(result){
|
||||
if(result.list && result.list.length > 0){
|
||||
$("#templateArea").hide();
|
||||
fn_renderRows(result.list, false);
|
||||
} else {
|
||||
$("#templateArea").show();
|
||||
addTotalRow();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fn_loadTemplateTasks(templateObjid){
|
||||
$.ajax({
|
||||
url: "/productionplanning/getWbsTemplateTasks.do",
|
||||
type: "POST", data: { templateObjid: templateObjid },
|
||||
dataType: "json",
|
||||
success: function(result){
|
||||
if(result.list && result.list.length > 0){
|
||||
// 기존 행 제거 후 템플릿 데이터로 재구성
|
||||
$("#wbsTaskList").empty();
|
||||
fn_renderRows(result.list, true);
|
||||
$("#templateArea").hide();
|
||||
} else {
|
||||
Swal.fire({title:'알림', text:'템플릿에 등록된 태스크가 없습니다.', icon:'info'});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function fn_renderRows(list, isTemplate){
|
||||
var hasTotalRow = false;
|
||||
for(var i=0; i<list.length; i++){
|
||||
var d = list[i];
|
||||
var level = String(d.TASK_LEVEL || d.task_level || '');
|
||||
var depth = parseInt(level) || 0;
|
||||
|
||||
if(depth === 0){
|
||||
hasTotalRow = true;
|
||||
var totalUserId = d.USER_ID || d.user_id || '';
|
||||
addTotalRow(totalUserId);
|
||||
continue;
|
||||
}
|
||||
|
||||
var objId = isTemplate ? generateObjId() : (d.OBJID || d.objid || generateObjId());
|
||||
var unitNo = d.UNIT_NO || d.unit_no || '';
|
||||
var taskName = d.TASK_NAME || d.task_name || '';
|
||||
var userId = d.USER_ID || d.user_id || '';
|
||||
var planStart = d.PLAN_START || d.plan_start || '';
|
||||
var planEnd = d.PLAN_END || d.plan_end || '';
|
||||
var actStart = d.ACT_START || d.act_start || '';
|
||||
var actEnd = d.ACT_END || d.act_end || '';
|
||||
var remark = d.REMARK || d.remark || '';
|
||||
var upperTask = d.UPPER_TASK_OBJID || d.upper_task_objid || '';
|
||||
var tmplTask = isTemplate ? (d.OBJID || d.objid || '') : (d.TEMPLATE_TASK_OBJID || d.template_task_objid || '');
|
||||
var progress = d.PROGRESS || d.progress || '';
|
||||
|
||||
appendTaskRow(objId, depth, unitNo, taskName, userId, planStart, planEnd, actStart, actEnd, remark, upperTask, tmplTask, progress);
|
||||
}
|
||||
if(!hasTotalRow) addTotalRow();
|
||||
renumberAllRows();
|
||||
recalcAllParents();
|
||||
}
|
||||
|
||||
// TOTAL 행 추가
|
||||
function addTotalRow(userId){
|
||||
if($("#row_total").length > 0) return;
|
||||
var objId = generateObjId();
|
||||
var tr = '<tr id="row_total" class="row-total" data-depth="0">';
|
||||
tr += '<td></td>';
|
||||
tr += hiddens(objId, '0', '', '0', '');
|
||||
tr += '<td></td><td></td><td></td>';
|
||||
tr += '<td style="font-weight:bold;">TOTAL<input type="hidden" name="TASK_NAME_' + objId + '" value="TOTAL"></td>';
|
||||
tr += '<td><select name="USER_ID_' + objId + '" id="USER_ID_' + objId + '" class="user-select">' + userOptionsHtml + '</select></td>';
|
||||
tr += '<td class="calc-field" id="ps_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="pe_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="as_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="ae_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="pr_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="dd_' + objId + '"></td>';
|
||||
tr += '<td><input type="text" name="REMARK_' + objId + '" id="REMARK_' + objId + '" value="" style="width:95%;"></td>';
|
||||
tr += '</tr>';
|
||||
$("#wbsTaskList").prepend(tr);
|
||||
if(userId) $("#USER_ID_" + objId).val(userId);
|
||||
$("#USER_ID_" + objId).select2({ width: '100%', placeholder: '', allowClear: true });
|
||||
}
|
||||
|
||||
function hiddens(objId, unitNo, upperTask, level, tmplTask){
|
||||
var h = '';
|
||||
h += '<input type="hidden" name="WBS_TASK_OBJID" value="' + objId + '">';
|
||||
h += '<input type="hidden" name="UNIT_NO_' + objId + '" id="UNIT_NO_' + objId + '" value="' + unitNo + '">';
|
||||
h += '<input type="hidden" name="UPPER_TASK_OBJID_' + objId + '" id="UPPER_TASK_OBJID_' + objId + '" value="' + upperTask + '">';
|
||||
h += '<input type="hidden" name="TASK_LEVEL_' + objId + '" id="TASK_LEVEL_' + objId + '" value="' + level + '">';
|
||||
h += '<input type="hidden" name="TEMPLATE_TASK_OBJID_' + objId + '" id="TEMPLATE_TASK_OBJID_' + objId + '" value="' + tmplTask + '">';
|
||||
return h;
|
||||
}
|
||||
|
||||
function appendTaskRow(objId, depth, unitNo, taskName, userId, planStart, planEnd, actStart, actEnd, remark, upperTask, tmplTask, progress){
|
||||
var isLevel3 = (depth === 3);
|
||||
var rowClass = (depth === 1) ? 'row-level1' : (depth === 2) ? 'row-level2' : '';
|
||||
|
||||
var tr = '<tr id="row_' + objId + '" class="' + rowClass + '">';
|
||||
tr += '<td><input type="checkbox" name="rowCheck" value="' + objId + '"></td>';
|
||||
tr += hiddens(objId, unitNo, upperTask, depth, tmplTask);
|
||||
tr += '<td><input type="text" class="lvl_input" data-objid="' + objId + '" data-level="1" style="width:80%;text-align:center;"' + (depth==1 ? ' value="'+unitNo+'"' : '') + '></td>';
|
||||
tr += '<td><input type="text" class="lvl_input" data-objid="' + objId + '" data-level="2" style="width:80%;text-align:center;"' + (depth==2 ? ' value="'+unitNo+'"' : '') + '></td>';
|
||||
tr += '<td><input type="text" class="lvl_input" data-objid="' + objId + '" data-level="3" style="width:80%;text-align:center;"' + (depth==3 ? ' value="'+unitNo+'"' : '') + '></td>';
|
||||
tr += '<td><input type="text" name="TASK_NAME_' + objId + '" id="TASK_NAME_' + objId + '" value="' + escapeHtml(taskName) + '"></td>';
|
||||
|
||||
tr += '<td><select name="USER_ID_' + objId + '" id="USER_ID_' + objId + '" class="user-select">' + userOptionsHtml + '</select></td>';
|
||||
|
||||
if(isLevel3){
|
||||
tr += '<td><input type="text" class="date_input" name="PLAN_START_' + objId + '" id="PLAN_START_' + objId + '" value="' + planStart + '" autocomplete="off"></td>';
|
||||
tr += '<td><input type="text" class="date_input" name="PLAN_END_' + objId + '" id="PLAN_END_' + objId + '" value="' + planEnd + '" autocomplete="off"></td>';
|
||||
tr += '<td><input type="text" class="date_input" name="ACT_START_' + objId + '" id="ACT_START_' + objId + '" value="' + actStart + '" autocomplete="off"></td>';
|
||||
tr += '<td><input type="text" class="date_input" name="ACT_END_' + objId + '" id="ACT_END_' + objId + '" value="' + actEnd + '" autocomplete="off"></td>';
|
||||
} else {
|
||||
tr += '<td class="calc-field" id="ps_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="pe_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="as_' + objId + '"></td>';
|
||||
tr += '<td class="calc-field" id="ae_' + objId + '"></td>';
|
||||
}
|
||||
|
||||
// 진척율: Level 3은 직접 입력, 나머지는 하위 평균 자동 계산
|
||||
if(isLevel3){
|
||||
var prVal = (progress != null && progress !== '') ? progress : '0';
|
||||
tr += '<td><input type="text" name="PROGRESS_' + objId + '" id="PROGRESS_' + objId + '" value="' + prVal + '" maxlength="3" style="width:35px;text-align:center;font-size:11px;" oninput="this.value=this.value.replace(/[^0-9]/g,\'\')" onkeydown="if(event.keyCode===13){this.blur();}" onblur="validateProgress(this);recalcAllParents()"></td>';
|
||||
} else {
|
||||
tr += '<td class="calc-field" id="pr_' + objId + '"></td>';
|
||||
}
|
||||
tr += '<td class="calc-field" id="dd_' + objId + '"></td>';
|
||||
tr += '<td><input type="text" name="REMARK_' + objId + '" id="REMARK_' + objId + '" value="' + escapeHtml(remark) + '" style="width:95%;"></td>';
|
||||
tr += '</tr>';
|
||||
|
||||
$("#wbsTaskList").append(tr);
|
||||
if(userId) $("#USER_ID_" + objId).val(userId);
|
||||
$("#USER_ID_" + objId).select2({ width: '100%', placeholder: '', allowClear: true });
|
||||
bindLevelInput(objId);
|
||||
if(isLevel3) bindDatePicker(objId);
|
||||
}
|
||||
|
||||
function bindDatePicker(objId){
|
||||
var opts = { changeMonth:true, changeYear:true, dateFormat:'yy-mm-dd',
|
||||
onSelect: function(){ recalcAllParents(); }
|
||||
};
|
||||
$("#PLAN_START_" + objId).datepicker(opts);
|
||||
$("#PLAN_END_" + objId).datepicker(opts);
|
||||
$("#ACT_START_" + objId).datepicker(opts);
|
||||
$("#ACT_END_" + objId).datepicker(opts);
|
||||
}
|
||||
|
||||
function bindLevelInput(objId){
|
||||
$("#row_" + objId + " .lvl_input").on("input", function(){
|
||||
var curObjId = $(this).data("objid");
|
||||
var level = $(this).data("level");
|
||||
$("#row_" + curObjId + " .lvl_input").not(this).val("");
|
||||
$("#UNIT_NO_" + curObjId).val($(this).val());
|
||||
$("#TASK_LEVEL_" + curObjId).val($(this).val() ? level : "");
|
||||
|
||||
var tr = $("#row_" + curObjId);
|
||||
tr.removeClass('row-level1 row-level2');
|
||||
if(level == 1) tr.addClass('row-level1');
|
||||
else if(level == 2) tr.addClass('row-level2');
|
||||
});
|
||||
}
|
||||
|
||||
function generateObjId(){
|
||||
return String(Math.abs(Math.floor(Math.random() * 2147483647))) + String(++rowSeq);
|
||||
}
|
||||
|
||||
function getRowDepth(tr){
|
||||
var objId = $(tr).find("input[name='WBS_TASK_OBJID']").val();
|
||||
var taskLevel = $.trim($("#TASK_LEVEL_" + objId).val());
|
||||
if(taskLevel !== "") return parseInt(taskLevel);
|
||||
var unitNo = $.trim($("#UNIT_NO_" + objId).val());
|
||||
if(unitNo === "0") return 0;
|
||||
if(unitNo === "") return -1;
|
||||
return (unitNo.match(/\./g) || []).length + 1;
|
||||
}
|
||||
|
||||
function findLastDescendant(parentTr){
|
||||
var parentDepth = getRowDepth(parentTr[0]);
|
||||
var last = parentTr;
|
||||
parentTr.nextAll("tr").each(function(){
|
||||
if(getRowDepth(this) > parentDepth) last = $(this);
|
||||
else return false;
|
||||
});
|
||||
return last;
|
||||
}
|
||||
|
||||
function renumberAllRows(){
|
||||
var rows = $("#wbsTaskList tr:not(#row_total)");
|
||||
var counters = [0, 0, 0];
|
||||
rows.each(function(){
|
||||
var objId = $(this).find("input[name='WBS_TASK_OBJID']").val();
|
||||
var depth = parseInt($("#TASK_LEVEL_" + objId).val()) || 0;
|
||||
if(depth < 1 || depth > 3) return true;
|
||||
|
||||
if(depth === 1){ counters[0]++; counters[1]=0; counters[2]=0; }
|
||||
else if(depth === 2){ counters[1]++; counters[2]=0; }
|
||||
else { counters[2]++; }
|
||||
|
||||
var unitNo;
|
||||
if(depth === 1) unitNo = String(counters[0]);
|
||||
else if(depth === 2) unitNo = counters[0] + "." + counters[1];
|
||||
else unitNo = counters[0] + "." + counters[1] + "." + counters[2];
|
||||
|
||||
$("#UNIT_NO_" + objId).val(unitNo);
|
||||
$(this).find(".lvl_input").val("");
|
||||
$(this).find(".lvl_input[data-level='" + depth + "']").val(unitNo);
|
||||
});
|
||||
}
|
||||
|
||||
// --- 행 추가/삭제 ---
|
||||
function addRow(){
|
||||
var objId = generateObjId();
|
||||
var checked = $("input[name='rowCheck']:checked");
|
||||
var autoLevel = "";
|
||||
var insertAfterTr = null;
|
||||
|
||||
if(checked.length > 0){
|
||||
var selectedTr = checked.last().closest("tr");
|
||||
autoLevel = getRowDepth(selectedTr[0]);
|
||||
insertAfterTr = findLastDescendant(selectedTr);
|
||||
checked.prop("checked", false);
|
||||
}
|
||||
|
||||
var depth = parseInt(autoLevel) || 3;
|
||||
appendTaskRow(objId, depth, '', '', '', '', '', '', '', '', '', '', '');
|
||||
|
||||
if(insertAfterTr){
|
||||
// detach 전 select2 제거 후 재초기화
|
||||
$("#USER_ID_" + objId).select2('destroy');
|
||||
var newRow = $("#row_" + objId).detach();
|
||||
insertAfterTr.after(newRow);
|
||||
$("#USER_ID_" + objId).select2({ width: '100%', placeholder: '', allowClear: true });
|
||||
bindLevelInput(objId);
|
||||
if(depth === 3) bindDatePicker(objId);
|
||||
}
|
||||
|
||||
$("#TASK_LEVEL_" + objId).val(depth);
|
||||
var tr = $("#row_" + objId);
|
||||
tr.removeClass('row-level1 row-level2');
|
||||
if(depth === 1) tr.addClass('row-level1');
|
||||
else if(depth === 2) tr.addClass('row-level2');
|
||||
|
||||
renumberAllRows();
|
||||
}
|
||||
|
||||
function addChildRow(){
|
||||
var checked = $("input[name='rowCheck']:checked");
|
||||
if(checked.length == 0){ Swal.fire('부모 행을 선택해 주세요'); return; }
|
||||
var selectedTr = checked.last().closest("tr");
|
||||
var selectedDepth = getRowDepth(selectedTr[0]);
|
||||
if(selectedDepth >= 3){ Swal.fire('수준 3 이하로는 추가할 수 없습니다'); return; }
|
||||
|
||||
var objId = generateObjId();
|
||||
var childDepth = selectedDepth + 1;
|
||||
var insertAfterTr = findLastDescendant(selectedTr);
|
||||
|
||||
appendTaskRow(objId, childDepth, '', '', '', '', '', '', '', '', '', '', '');
|
||||
$("#USER_ID_" + objId).select2('destroy');
|
||||
var newRow = $("#row_" + objId).detach();
|
||||
insertAfterTr.after(newRow);
|
||||
$("#USER_ID_" + objId).select2({ width: '100%', placeholder: '', allowClear: true });
|
||||
bindLevelInput(objId);
|
||||
if(childDepth === 3) bindDatePicker(objId);
|
||||
|
||||
$("#TASK_LEVEL_" + objId).val(childDepth);
|
||||
var tr = $("#row_" + objId);
|
||||
tr.removeClass('row-level1 row-level2');
|
||||
if(childDepth === 1) tr.addClass('row-level1');
|
||||
else if(childDepth === 2) tr.addClass('row-level2');
|
||||
|
||||
checked.prop("checked", false);
|
||||
renumberAllRows();
|
||||
}
|
||||
|
||||
function deleteRow(){
|
||||
var checked = $("input[name='rowCheck']:checked");
|
||||
if(checked.length == 0){ Swal.fire('삭제할 행을 선택해 주세요'); return; }
|
||||
var removeTargets = [];
|
||||
var hasChildren = false;
|
||||
checked.each(function(){
|
||||
var objId = $(this).val();
|
||||
var tr = $("#row_" + objId);
|
||||
if(tr.attr("id") === "row_total") return true;
|
||||
removeTargets.push(tr);
|
||||
var parentDepth = getRowDepth(tr[0]);
|
||||
tr.nextAll("tr").each(function(){
|
||||
if(getRowDepth(this) > parentDepth){ removeTargets.push($(this)); hasChildren = true; }
|
||||
else return false;
|
||||
});
|
||||
});
|
||||
var msg = hasChildren ? '하위 항목도 함께 삭제됩니다. 삭제하시겠습니까?' : '삭제하시겠습니까?';
|
||||
Swal.fire({ title: msg, icon:'warning', showCancelButton:true, confirmButtonText:'삭제', cancelButtonText:'취소'
|
||||
}).then(function(result){
|
||||
if(result.isConfirmed){
|
||||
$.each(removeTargets, function(i, tr){ tr.remove(); });
|
||||
renumberAllRows();
|
||||
recalcAllParents();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --- 자동 계산 ---
|
||||
function recalcAllParents(){
|
||||
var rows = $("#wbsTaskList tr");
|
||||
var allItems = [];
|
||||
|
||||
rows.each(function(){
|
||||
var objId = $(this).find("input[name='WBS_TASK_OBJID']").val();
|
||||
if(!objId) return true;
|
||||
var depth = getRowDepth(this);
|
||||
var item = { objId: objId, depth: depth, tr: $(this) };
|
||||
|
||||
if(depth === 3){
|
||||
item.planStart = $.trim($("#PLAN_START_" + objId).val());
|
||||
item.planEnd = $.trim($("#PLAN_END_" + objId).val());
|
||||
item.actStart = $.trim($("#ACT_START_" + objId).val());
|
||||
item.actEnd = $.trim($("#ACT_END_" + objId).val());
|
||||
var prVal = $.trim($("#PROGRESS_" + objId).val());
|
||||
item.progress = (prVal !== '') ? Number(prVal) : 0;
|
||||
item.delay = calcDelay(item);
|
||||
}
|
||||
allItems.push(item);
|
||||
});
|
||||
|
||||
// Level 2: 하위 Level 3 집계
|
||||
for(var i=0; i<allItems.length; i++){
|
||||
if(allItems[i].depth === 2) calcParentFromChildren(allItems, i, 3);
|
||||
}
|
||||
// Level 1: 하위 Level 2 집계
|
||||
for(var i=0; i<allItems.length; i++){
|
||||
if(allItems[i].depth === 1) calcParentFromChildren(allItems, i, 2);
|
||||
}
|
||||
// TOTAL: 하위 Level 1 집계
|
||||
for(var i=0; i<allItems.length; i++){
|
||||
if(allItems[i].depth === 0){
|
||||
var children = allItems.filter(function(x){ return x.depth === 1; });
|
||||
calcParentAggregate(allItems[i], children);
|
||||
}
|
||||
}
|
||||
|
||||
// DOM에 반영
|
||||
for(var i=0; i<allItems.length; i++){
|
||||
var it = allItems[i];
|
||||
if(it.depth < 3){
|
||||
$("#ps_" + it.objId).text(it.planStart || '');
|
||||
$("#pe_" + it.objId).text(it.planEnd || '');
|
||||
$("#as_" + it.objId).text(it.actStart || '');
|
||||
$("#ae_" + it.objId).text(it.actEnd || '');
|
||||
}
|
||||
if(it.depth < 3){
|
||||
var prVal = (it.progress != null && it.progress !== '') ? it.progress : 0;
|
||||
$("#pr_" + it.objId).text(Number(prVal).toFixed(1));
|
||||
}
|
||||
var ddText = '';
|
||||
if(it.delay != null && it.delay !== ''){
|
||||
if(it.delay > 0) ddText = '<span class="delay-plus">+' + it.delay + '</span>';
|
||||
else if(it.delay < 0) ddText = '<span class="delay-minus">' + it.delay + '</span>';
|
||||
else ddText = '0';
|
||||
}
|
||||
$("#dd_" + it.objId).html(ddText);
|
||||
}
|
||||
}
|
||||
|
||||
function calcParentFromChildren(allItems, parentIdx, childDepth){
|
||||
var parent = allItems[parentIdx];
|
||||
var children = [];
|
||||
for(var j=parentIdx+1; j<allItems.length; j++){
|
||||
if(allItems[j].depth <= parent.depth) break;
|
||||
if(allItems[j].depth === childDepth) children.push(allItems[j]);
|
||||
}
|
||||
calcParentAggregate(parent, children);
|
||||
}
|
||||
|
||||
function calcParentAggregate(parent, children){
|
||||
if(!children || children.length === 0){
|
||||
parent.planStart=''; parent.planEnd=''; parent.actStart=''; parent.actEnd=''; parent.progress=0; parent.delay='';
|
||||
return;
|
||||
}
|
||||
var ps=[], pe=[], as2=[], ae=[];
|
||||
var rateSum = 0;
|
||||
for(var i=0; i<children.length; i++){
|
||||
var c = children[i];
|
||||
if(c.planStart) ps.push(c.planStart);
|
||||
if(c.planEnd) pe.push(c.planEnd);
|
||||
if(c.actStart) as2.push(c.actStart);
|
||||
if(c.actEnd) ae.push(c.actEnd);
|
||||
rateSum += (c.progress != null && c.progress !== '') ? Number(c.progress) : 0;
|
||||
}
|
||||
parent.planStart = ps.length > 0 ? ps.sort()[0] : '';
|
||||
parent.planEnd = pe.length > 0 ? pe.sort().reverse()[0] : '';
|
||||
parent.actStart = as2.length > 0 ? as2.sort()[0] : '';
|
||||
parent.actEnd = ae.length > 0 ? ae.sort().reverse()[0] : '';
|
||||
parent.progress = rateSum / children.length;
|
||||
parent.delay = calcDelay(parent);
|
||||
}
|
||||
|
||||
function calcProgress3(item){
|
||||
if(item.actEnd) return 100;
|
||||
if(!item.actStart) return 0;
|
||||
if(!item.planStart || !item.planEnd) return 0;
|
||||
var today = new Date(); today.setHours(0,0,0,0);
|
||||
var s = new Date(item.planStart), e = new Date(item.planEnd), a = new Date(item.actStart);
|
||||
var total = (e - s) / 86400000;
|
||||
if(total <= 0) return 0;
|
||||
var elapsed = (today - a) / 86400000;
|
||||
return Math.max(0, Math.min(Math.round(elapsed / total * 100), 99));
|
||||
}
|
||||
|
||||
function calcDelay(item){
|
||||
if(!item.planEnd || item.progress === '' || item.progress == null) return '';
|
||||
if(Number(item.progress) === 100){
|
||||
if(!item.actEnd) return '';
|
||||
return Math.round((new Date(item.actEnd) - new Date(item.planEnd)) / 86400000);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function validateProgress(el){
|
||||
el.value = el.value.replace(/[^0-9]/g, '');
|
||||
var v = parseInt(el.value);
|
||||
if(isNaN(v) || v < 0) el.value = '0';
|
||||
else if(v > 100) el.value = '100';
|
||||
}
|
||||
|
||||
function escapeHtml(str){
|
||||
if(!str) return '';
|
||||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||||
}
|
||||
|
||||
function calculateParentRelations(){
|
||||
var rows = $("#wbsTaskList tr");
|
||||
var totalObjId = $("#row_total").find("input[name='WBS_TASK_OBJID']").val();
|
||||
rows.each(function(idx){
|
||||
if($(this).attr("id") === "row_total") return true;
|
||||
var objId = $(this).find("input[name='WBS_TASK_OBJID']").val();
|
||||
var depth = getRowDepth(this);
|
||||
var parentObjId = "";
|
||||
if(depth === 1){ parentObjId = totalObjId; }
|
||||
else if(depth > 1){
|
||||
$(this).prevAll("tr").each(function(){
|
||||
if(getRowDepth(this) === depth - 1){
|
||||
parentObjId = $(this).find("input[name='WBS_TASK_OBJID']").val();
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
$("#UPPER_TASK_OBJID_" + objId).val(parentObjId);
|
||||
});
|
||||
}
|
||||
|
||||
// --- 저장 ---
|
||||
function fn_save(){
|
||||
var taskCount = $("input[name='WBS_TASK_OBJID']").length;
|
||||
if(taskCount <= 1){ Swal.fire({title:'알림', text:'등록할 항목을 추가해 주세요.', icon:'warning'}); return; }
|
||||
|
||||
var isValid = true;
|
||||
$("input[name='WBS_TASK_OBJID']").each(function(){
|
||||
var objId = $(this).val();
|
||||
if($(this).closest("tr").attr("id") === "row_total") return true;
|
||||
var level = $.trim($("#UNIT_NO_" + objId).val());
|
||||
var taskName = $.trim($("#TASK_NAME_" + objId).val());
|
||||
if(level == "" || taskName == ""){ isValid = false; return false; }
|
||||
});
|
||||
if(!isValid){ Swal.fire('수준과 Unit Name / 공정을 모두 입력해 주세요'); return; }
|
||||
|
||||
calculateParentRelations();
|
||||
recalcAllParents();
|
||||
|
||||
var saveData = [];
|
||||
var totalUserId = '';
|
||||
$("#wbsTaskList tr").each(function(){
|
||||
var objId = $(this).find("input[name='WBS_TASK_OBJID']").val();
|
||||
if(!objId) return true;
|
||||
var depth = getRowDepth(this);
|
||||
|
||||
// TOTAL 행은 담당자만 별도 저장
|
||||
if(depth === 0){
|
||||
totalUserId = $("#USER_ID_" + objId).val() || '';
|
||||
return true;
|
||||
}
|
||||
|
||||
var row = {
|
||||
objid: objId,
|
||||
taskName: $.trim($("#TASK_NAME_" + objId).val()),
|
||||
taskSeq: saveData.length + 1,
|
||||
taskLevel: String(depth),
|
||||
unitNo: $.trim($("#UNIT_NO_" + objId).val()),
|
||||
upperTaskObjid: $.trim($("#UPPER_TASK_OBJID_" + objId).val()),
|
||||
templateTaskObjid: $.trim($("#TEMPLATE_TASK_OBJID_" + objId).val()),
|
||||
remark: $.trim($("#REMARK_" + objId).val())
|
||||
};
|
||||
|
||||
row.userId = $("#USER_ID_" + objId).val() || '';
|
||||
if(depth === 3){
|
||||
row.planStart = $.trim($("#PLAN_START_" + objId).val());
|
||||
row.planEnd = $.trim($("#PLAN_END_" + objId).val());
|
||||
row.actStart = $.trim($("#ACT_START_" + objId).val());
|
||||
row.actEnd = $.trim($("#ACT_END_" + objId).val());
|
||||
row.progress = $.trim($("#PROGRESS_" + objId).val());
|
||||
} else {
|
||||
row.progress = $.trim($("#pr_" + objId).text()).replace('%', '');
|
||||
row.planStart = $.trim($("#ps_" + objId).text());
|
||||
row.planEnd = $.trim($("#pe_" + objId).text());
|
||||
row.actStart = $.trim($("#as_" + objId).text());
|
||||
row.actEnd = $.trim($("#ae_" + objId).text());
|
||||
}
|
||||
saveData.push(row);
|
||||
});
|
||||
|
||||
Swal.fire({ title:'저장하시겠습니까?', icon:'question', showCancelButton:true, confirmButtonText:'확인', cancelButtonText:'취소'
|
||||
}).then(function(result){
|
||||
if(result.isConfirmed){
|
||||
$.ajax({
|
||||
url: "/productionplanning/saveEquipWbsAssign.do",
|
||||
type: "POST", contentType: "application/json",
|
||||
data: JSON.stringify({ projectObjid: projectObjid, wbsType: wbsType, templateObjid: $("#wbsTemplateSelect").val() || '', totalUserId: totalUserId, tasks: saveData }),
|
||||
dataType: "json",
|
||||
success: function(res){
|
||||
if(res.success){
|
||||
Swal.fire({title:'완료', text:'저장되었습니다.', icon:'success'}).then(function(){
|
||||
if(opener && opener.fn_search) opener.fn_search();
|
||||
window.close();
|
||||
});
|
||||
} else {
|
||||
Swal.fire({title:'오류', text: res.message || '저장 실패', icon:'error'});
|
||||
}
|
||||
},
|
||||
error: function(){ Swal.fire({title:'오류', text:'서버 오류', icon:'error'}); }
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="info-area">
|
||||
<table>
|
||||
<tr>
|
||||
<td><label>프로젝트번호:</label></td>
|
||||
<td>${projectInfo.PROJECT_NO}</td>
|
||||
<td style="padding-left:20px;"><label>WBS유형:</label></td>
|
||||
<td><span style="font-weight:bold; color:#1976D2;" id="wbsTypeLabel"></span></td>
|
||||
<td style="padding-left:20px;"><label>품번:</label></td>
|
||||
<td>${projectInfo.PART_NO}</td>
|
||||
<td style="padding-left:20px;"><label>품명:</label></td>
|
||||
<td>${projectInfo.PART_NAME}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="templateArea" style="padding:0 10px 5px; display:none;">
|
||||
<label>WBS 템플릿:</label>
|
||||
<select id="wbsTemplateSelect" style="width:250px;">
|
||||
<option value="">-- 선택 --</option>
|
||||
${wbsTemplateOptions}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div style="padding:0 10px;">
|
||||
<div class="plm_btn_wrap" style="margin-bottom:5px;">
|
||||
<input type="button" value="추가" class="plm_btns" onclick="addRow();">
|
||||
<input type="button" value="하위추가" class="plm_btns" onclick="addChildRow();">
|
||||
<input type="button" value="삭제" class="plm_btns" onclick="deleteRow();">
|
||||
<input type="button" value="저장" class="plm_btns" id="btnSave">
|
||||
<input type="button" value="닫기" class="plm_btns" id="btnClose">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="width:100%; padding:0 10px;">
|
||||
<table class="plm_table" style="width:100%;border-collapse:collapse;">
|
||||
<colgroup>
|
||||
<col width="3%"/><col width="4%"/><col width="4%"/><col width="5%"/>
|
||||
<col width="11%"/><col width="10%"/>
|
||||
<col width="8%"/><col width="8%"/><col width="8%"/><col width="8%"/>
|
||||
<col width="5%"/><col width="5%"/><col width="*"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="plm_thead">
|
||||
<td rowspan="2">선택</td>
|
||||
<td colspan="3">수준</td>
|
||||
<td rowspan="2">Unit Name / 공정</td>
|
||||
<td rowspan="2">담당자</td>
|
||||
<td colspan="2" id="thPlanGroup">계획</td>
|
||||
<td colspan="2" id="thActGroup">실적</td>
|
||||
<td rowspan="2">진척율<br/>(%)</td>
|
||||
<td rowspan="2">지연일수</td>
|
||||
<td rowspan="2">비고</td>
|
||||
</tr>
|
||||
<tr class="plm_thead">
|
||||
<td>1</td><td>2</td><td>3</td>
|
||||
<td>시작일</td><td>완료일</td>
|
||||
<td>시작일</td><td>완료일</td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
<div style="width:100%;height:calc(100vh - 180px);overflow-y:auto;padding:0 10px;">
|
||||
<table class="plm_table" style="width:100%;border-collapse:collapse;">
|
||||
<colgroup>
|
||||
<col width="3%"/><col width="4%"/><col width="4%"/><col width="5%"/>
|
||||
<col width="11%"/><col width="10%"/>
|
||||
<col width="8%"/><col width="8%"/><col width="8%"/><col width="8%"/>
|
||||
<col width="5%"/><col width="5%"/><col width="*"/>
|
||||
</colgroup>
|
||||
<tbody id="wbsTaskList"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 헤더에 WBS유형 표시
|
||||
var planLabel = wbsTypeLabel;
|
||||
$("#thPlanGroup").text(planLabel + ' 계획');
|
||||
$("#thActGroup").text(planLabel + ' 실적');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -24,6 +24,11 @@ String connector = person.getUserId();
|
||||
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
|
||||
|
||||
<c:set var="connector" value="<%=connector %>" />
|
||||
<%
|
||||
// DB에서 메뉴명 조회 (공통 유틸 사용)
|
||||
String menuObjId = request.getParameter("menuObjId");
|
||||
String menuName = CommonUtils.getMenuName(menuObjId, "품목별 입고 관리");
|
||||
%>
|
||||
<style>
|
||||
::-webkit-scrollbar {
|
||||
display: none;
|
||||
@@ -100,7 +105,7 @@ var columns = [
|
||||
// fn_openTemplateMasterPopUp(objid);
|
||||
// }
|
||||
// },
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '250', title : 'UNIT', field : 'WBS_TASK_CNT0',
|
||||
{headerHozAlign : 'center', hozAlign : 'center', width : '250', title : 'WBS', field : 'WBS_TASK_CNT0',
|
||||
formatter:fnc_getFolderIcon,
|
||||
cellClick:function(e, cell){
|
||||
var objid = fnc_checkNull(cell.getData().OBJID);
|
||||
@@ -342,7 +347,7 @@ function openProjectFormPopUp(objId){
|
||||
<div class="content-box-s">
|
||||
<div class="plm_menu_name_gdnsi">
|
||||
<h2>
|
||||
<span>프로젝트관리_제품구분_UNIT관리</span>
|
||||
<span><%=menuName%></span>
|
||||
</h2>
|
||||
<div class="btnArea">
|
||||
<input type="button" value="삭제" class="plm_btns delete" id="btnDelete">
|
||||
|
||||
@@ -1719,6 +1719,154 @@ public class ProductionPlanningController extends BaseService {
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산관리 -> 생산계획&실적관리(장비) 목록
|
||||
*/
|
||||
@RequestMapping("/productionplanning/prodPlanResultMgmtEquipList.do")
|
||||
public String prodPlanResultMgmtEquipList(HttpServletRequest request, @RequestParam Map paramMap){
|
||||
Map code_map = new HashMap();
|
||||
try{
|
||||
code_map.put("project_no", commonService.bizMakeOptionList("", CommonUtils.nullToEmpty((String)paramMap.get("project_no")), "common.getCusProjectNoList"));
|
||||
code_map.put("product_cd", commonService.bizMakeOptionList("0000001", CommonUtils.nullToEmpty((String)paramMap.get("product_code")), "common.getCodeselect"));
|
||||
code_map.put("category_cd", commonService.bizMakeOptionList("0000167", CommonUtils.nullToEmpty((String)paramMap.get("category_code")), "common.getCodeselect"));
|
||||
code_map.put("production_type_cd", commonService.bizMakeOptionList("0001832", CommonUtils.nullToEmpty((String)paramMap.get("production_type")), "common.getCodeselect"));
|
||||
code_map.put("customer_cd", commonService.bizMakeOptionList("", CommonUtils.nullToEmpty((String)paramMap.get("customer_objid")), "common.getsupplyselect"));
|
||||
|
||||
// WBS 템플릿 목록 (Machine 제품용)
|
||||
List wbsTemplateList = commonService.selectList("productionplanning.getWbsTemplateOptionList", request, paramMap);
|
||||
StringBuilder wbsTemplateSb = new StringBuilder();
|
||||
if(wbsTemplateList != null) {
|
||||
for(int i=0; i<wbsTemplateList.size(); i++) {
|
||||
Map row = (Map)wbsTemplateList.get(i);
|
||||
wbsTemplateSb.append("<option value=\"").append(row.get("OBJID")).append("\">").append(row.get("TITLE")).append("</option>");
|
||||
}
|
||||
}
|
||||
code_map.put("wbs_template", wbsTemplateSb.toString());
|
||||
|
||||
request.setAttribute("code_map", code_map);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "/productionplanning/prodPlanResultMgmtEquipList";
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산관리 -> 생산계획&실적관리(장비) 그리드 목록
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/prodPlanResultMgmtEquipGridList.do")
|
||||
public Map prodPlanResultMgmtEquipGridList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
String[] projectNos = request.getParameterValues("search_project_no");
|
||||
if(projectNos != null && projectNos.length > 0) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for(int i = 0; i < projectNos.length; i++) {
|
||||
if(i > 0) sb.append(",");
|
||||
sb.append(projectNos[i]);
|
||||
}
|
||||
paramMap.put("search_project_nos", sb.toString());
|
||||
}
|
||||
|
||||
commonService.selectListPagingNew("productionplanning.prodPlanResultMgmtEquipGridList", request, paramMap);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 장비 WBS할당 팝업
|
||||
*/
|
||||
@RequestMapping("/productionplanning/prodPlanWbsAssignPopup.do")
|
||||
public String prodPlanWbsAssignPopup(HttpServletRequest request, @RequestParam Map paramMap){
|
||||
try{
|
||||
// 프로젝트 정보 조회
|
||||
Map projectInfo = commonService.selectOne("productionplanning.getEquipProjectInfo", request, paramMap);
|
||||
request.setAttribute("projectInfo", projectInfo);
|
||||
|
||||
// WBS 템플릿 목록
|
||||
List templateList = commonService.selectList("productionplanning.getWbsTemplateOptionList", request, paramMap);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(templateList != null){
|
||||
for(int i=0; i<templateList.size(); i++){
|
||||
Map row = (Map)templateList.get(i);
|
||||
sb.append("<option value=\"").append(row.get("OBJID")).append("\">").append(row.get("TITLE")).append("</option>");
|
||||
}
|
||||
}
|
||||
request.setAttribute("wbsTemplateOptions", sb.toString());
|
||||
|
||||
// 사용자 목록
|
||||
List userList = commonService.selectList("common.getUserselect6", request, new HashMap());
|
||||
request.setAttribute("userList", userList);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "/productionplanning/prodPlanWbsAssignPopup";
|
||||
}
|
||||
|
||||
/**
|
||||
* 장비 WBS 템플릿 태스크 목록 조회
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/getWbsTemplateTasks.do")
|
||||
public Map getWbsTemplateTasks(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map result = new HashMap();
|
||||
try{
|
||||
List list = commonService.selectList("productionplanning.getWbsTemplateTasks", request, paramMap);
|
||||
result.put("list", list);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
result.put("list", new ArrayList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 장비 프로젝트의 기존 WBS 태스크 목록 조회
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/getEquipWbsTaskList.do")
|
||||
public Map getEquipWbsTaskList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
|
||||
Map result = new HashMap();
|
||||
try{
|
||||
List list = commonService.selectList("productionplanning.getEquipWbsTaskList", request, paramMap);
|
||||
result.put("list", list);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
result.put("list", new ArrayList());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 장비 WBS 할당 저장
|
||||
*/
|
||||
@ResponseBody
|
||||
@RequestMapping("/productionplanning/saveEquipWbsAssign.do")
|
||||
public Map saveEquipWbsAssign(HttpServletRequest request, @RequestBody Map<String, Object> paramMap){
|
||||
try{
|
||||
HttpSession session = request.getSession();
|
||||
PersonBean loginUser = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
|
||||
if(loginUser != null){
|
||||
paramMap.put("writer", loginUser.getUserId());
|
||||
} else {
|
||||
paramMap.put("writer", "");
|
||||
}
|
||||
|
||||
List<Map<String, Object>> tasks = (List<Map<String, Object>>)paramMap.get("tasks");
|
||||
if(tasks == null || tasks.isEmpty()){
|
||||
Map result = new HashMap();
|
||||
result.put("success", false);
|
||||
result.put("message", "저장할 태스크가 없습니다.");
|
||||
return result;
|
||||
}
|
||||
|
||||
return productionPlanningService.saveEquipWbsAssign(paramMap);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
Map result = new HashMap();
|
||||
result.put("success", false);
|
||||
result.put("message", e.getMessage());
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 생산계획 생성 팝업
|
||||
* @param request
|
||||
|
||||
@@ -4603,6 +4603,7 @@
|
||||
LEFT OUTER JOIN PRODUCTION_PLAN PP ON PP.PROJECT_OBJID = PM.OBJID
|
||||
AND PP.STATUS = 'active'
|
||||
WHERE PM.PROJECT_NO IS NOT NULL AND PM.PROJECT_NO != ''
|
||||
AND PM.PRODUCT != '0000928'
|
||||
|
||||
UNION ALL
|
||||
|
||||
@@ -4712,6 +4713,276 @@
|
||||
</if>
|
||||
ORDER BY T.SORT_DATE DESC, T.PROJECT_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 장비 WBS 템플릿 옵션 목록 -->
|
||||
<select id="getWbsTemplateOptionList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT OBJID, TITLE
|
||||
FROM PMS_WBS_TEMPLATE
|
||||
WHERE 1=1
|
||||
ORDER BY TITLE
|
||||
</select>
|
||||
|
||||
<!-- 장비 프로젝트 기본정보 조회 -->
|
||||
<select id="getEquipProjectInfo" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
PM.OBJID,
|
||||
PM.PROJECT_NO,
|
||||
COALESCE(CI.PART_NO, PM.PART_NO, '') AS PART_NO,
|
||||
COALESCE(CI.PART_NAME, PM.PART_NAME, '') AS PART_NAME
|
||||
FROM PROJECT_MGMT PM
|
||||
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
|
||||
AND PM.PART_OBJID = CI.PART_OBJID AND CI.STATUS = 'ACTIVE'
|
||||
WHERE PM.OBJID::VARCHAR = #{projectObjid}::VARCHAR
|
||||
</select>
|
||||
|
||||
<!-- 장비 WBS 템플릿 태스크 목록 조회 -->
|
||||
<select id="getWbsTemplateTasks" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.PARENT_OBJID,
|
||||
T.TASK_NAME,
|
||||
T.TASK_SEQ,
|
||||
T.TASK_LEVEL,
|
||||
T.USER_ID,
|
||||
T.UNIT_NO,
|
||||
T.UPPER_TASK_OBJID
|
||||
FROM PMS_WBS_TASK_STANDARD T
|
||||
WHERE T.PARENT_OBJID = #{templateObjid}
|
||||
ORDER BY CAST(T.TASK_SEQ AS INTEGER)
|
||||
</select>
|
||||
|
||||
<!-- 장비 프로젝트의 기존 WBS 태스크 목록 조회 -->
|
||||
<select id="getEquipWbsTaskList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT
|
||||
T.OBJID,
|
||||
T.CONTRACT_OBJID,
|
||||
T.PARENT_OBJID,
|
||||
T.TASK_NAME,
|
||||
T.TASK_SEQ,
|
||||
T.UNIT_NO,
|
||||
COALESCE(T.TASK_LEVEL, '') AS TASK_LEVEL,
|
||||
COALESCE(T.UPPER_TASK_OBJID, '') AS UPPER_TASK_OBJID,
|
||||
COALESCE(T.TEMPLATE_TASK_OBJID, '') AS TEMPLATE_TASK_OBJID,
|
||||
CASE WHEN #{wbsType} = 'PRODUCE' THEN COALESCE(T.PRODUCE_USER_ID, '')
|
||||
ELSE COALESCE(T.SHIP_USER_ID, '') END AS USER_ID,
|
||||
CASE WHEN #{wbsType} = 'PRODUCE' THEN COALESCE(T.PRODUCE_PLAN_START, '')
|
||||
ELSE COALESCE(T.SHIP_PLAN_START, '') END AS PLAN_START,
|
||||
CASE WHEN #{wbsType} = 'PRODUCE' THEN COALESCE(T.PRODUCE_PLAN_END, '')
|
||||
ELSE COALESCE(T.SHIP_PLAN_END, '') END AS PLAN_END,
|
||||
CASE WHEN #{wbsType} = 'PRODUCE' THEN COALESCE(T.PRODUCE_ACT_START, '')
|
||||
ELSE COALESCE(T.SHIP_ACT_START, '') END AS ACT_START,
|
||||
CASE WHEN #{wbsType} = 'PRODUCE' THEN COALESCE(T.PRODUCE_ACT_END, '')
|
||||
ELSE COALESCE(T.SHIP_ACT_END, '') END AS ACT_END,
|
||||
COALESCE(T.REMARK, '') AS REMARK,
|
||||
COALESCE(T.PROGRESS, '') AS PROGRESS
|
||||
FROM PMS_WBS_TASK T
|
||||
WHERE T.CONTRACT_OBJID = #{projectObjid}
|
||||
AND COALESCE(T.WBS_TYPE, '') = #{wbsType}
|
||||
ORDER BY CAST(COALESCE(NULLIF(T.TASK_SEQ,''), '0') AS INTEGER)
|
||||
</select>
|
||||
|
||||
<!-- 장비 WBS 태스크 삭제 -->
|
||||
<delete id="deleteEquipWbsTasks" parameterType="map">
|
||||
DELETE FROM PMS_WBS_TASK
|
||||
WHERE CONTRACT_OBJID = #{projectObjid}
|
||||
AND COALESCE(WBS_TYPE, '') = #{wbsType}
|
||||
</delete>
|
||||
|
||||
<!-- 장비 WBS 태스크 INSERT -->
|
||||
<insert id="insertEquipWbsTask" parameterType="map">
|
||||
INSERT INTO PMS_WBS_TASK (
|
||||
OBJID,
|
||||
CONTRACT_OBJID,
|
||||
PARENT_OBJID,
|
||||
TASK_NAME,
|
||||
TASK_SEQ,
|
||||
TASK_LEVEL,
|
||||
UNIT_NO,
|
||||
UPPER_TASK_OBJID,
|
||||
TEMPLATE_TASK_OBJID,
|
||||
WBS_TYPE,
|
||||
<if test="wbsType == 'PRODUCE'">
|
||||
PRODUCE_USER_ID,
|
||||
PRODUCE_PLAN_START,
|
||||
PRODUCE_PLAN_END,
|
||||
PRODUCE_ACT_START,
|
||||
PRODUCE_ACT_END,
|
||||
</if>
|
||||
<if test="wbsType == 'SHIP'">
|
||||
SHIP_USER_ID,
|
||||
SHIP_PLAN_START,
|
||||
SHIP_PLAN_END,
|
||||
SHIP_ACT_START,
|
||||
SHIP_ACT_END,
|
||||
</if>
|
||||
REMARK,
|
||||
PROGRESS,
|
||||
WRITER
|
||||
) VALUES (
|
||||
#{newObjid},
|
||||
#{projectObjid},
|
||||
'',
|
||||
#{taskName},
|
||||
#{taskSeq},
|
||||
#{taskLevel},
|
||||
#{unitNo},
|
||||
#{upperTaskObjid},
|
||||
#{templateTaskObjid},
|
||||
#{wbsType},
|
||||
<if test="wbsType == 'PRODUCE'">
|
||||
#{userId},
|
||||
#{planStart},
|
||||
#{planEnd},
|
||||
#{actStart},
|
||||
#{actEnd},
|
||||
</if>
|
||||
<if test="wbsType == 'SHIP'">
|
||||
#{userId},
|
||||
#{planStart},
|
||||
#{planEnd},
|
||||
#{actStart},
|
||||
#{actEnd},
|
||||
</if>
|
||||
#{remark},
|
||||
#{progress},
|
||||
#{writer}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 생산계획&실적관리(장비) 그리드 목록 조회 - Machine 제품만 -->
|
||||
<select id="prodPlanResultMgmtEquipGridList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
|
||||
SELECT * FROM (
|
||||
SELECT
|
||||
PM.OBJID,
|
||||
PM.PROJECT_NO,
|
||||
CM.PRODUCT,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PRODUCT LIMIT 1),
|
||||
''
|
||||
) AS PRODUCT_NAME,
|
||||
CM.CATEGORY_CD AS CATEGORY_CODE,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.CATEGORY_CD LIMIT 1),
|
||||
CM.CATEGORY_CD
|
||||
) AS CATEGORY_CODE_NAME,
|
||||
COALESCE(PP.PRODUCTION_TYPE, '') AS PRODUCTION_TYPE,
|
||||
COALESCE(
|
||||
(SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = PP.PRODUCTION_TYPE LIMIT 1),
|
||||
''
|
||||
) AS PRODUCTION_TYPE_NAME,
|
||||
CM.CUSTOMER_OBJID,
|
||||
COALESCE(
|
||||
CASE
|
||||
WHEN CM.CUSTOMER_OBJID LIKE 'C_%' THEN
|
||||
(SELECT CLIENT_NM FROM CLIENT_MNG AS C WHERE 'C_' || C.OBJID::VARCHAR = CM.CUSTOMER_OBJID LIMIT 1)
|
||||
ELSE
|
||||
(SELECT SUPPLY_NAME FROM SUPPLY_MNG WHERE OBJID::VARCHAR = CM.CUSTOMER_OBJID::VARCHAR LIMIT 1)
|
||||
END,
|
||||
''
|
||||
) AS CUSTOMER_NAME,
|
||||
COALESCE(CI.DUE_DATE, PM.DUE_DATE, CM.REQ_DEL_DATE) AS REQ_DEL_DATE,
|
||||
COALESCE(CI.CUSTOMER_REQUEST, '') AS CUSTOMER_REQUEST,
|
||||
COALESCE(CI.PART_NO, PM.PART_NO, '') AS PART_NO,
|
||||
COALESCE(CI.PART_NAME, PM.PART_NAME, '') AS PART_NAME,
|
||||
(SELECT
|
||||
CASE
|
||||
WHEN COUNT(*) = 0 THEN ''
|
||||
WHEN COUNT(*) = 1 THEN MIN(CIS.SERIAL_NO)
|
||||
ELSE MIN(CIS.SERIAL_NO) || ' 외 ' || (COUNT(*) - 1)::TEXT || '건'
|
||||
END
|
||||
FROM CONTRACT_ITEM_SERIAL CIS
|
||||
WHERE CIS.ITEM_OBJID = CI.OBJID
|
||||
AND UPPER(CIS.STATUS) = 'ACTIVE'
|
||||
AND CIS.SERIAL_NO IS NOT NULL) AS SERIAL_NO,
|
||||
-- 생산WBS: WBS_TYPE='PRODUCE'인 태스크 존재 여부
|
||||
(SELECT COUNT(1) FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'PRODUCE'
|
||||
) AS PROD_WBS_CNT,
|
||||
-- 생산진척율: Level1 PROGRESS 평균 (계층별 집계 결과)
|
||||
CASE
|
||||
WHEN (SELECT COUNT(1) FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'PRODUCE' AND O.TASK_LEVEL = '1') = 0 THEN 0
|
||||
ELSE ROUND(
|
||||
(SELECT COALESCE(AVG(CASE WHEN O.PROGRESS IS NOT NULL AND O.PROGRESS != ''
|
||||
THEN CAST(O.PROGRESS AS numeric) ELSE 0 END), 0)
|
||||
FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'PRODUCE' AND O.TASK_LEVEL = '1')
|
||||
, 1)
|
||||
END AS PROD_PROGRESS_RATE,
|
||||
-- 납품WBS: WBS_TYPE='SHIP'인 태스크 존재 여부
|
||||
(SELECT COUNT(1) FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'SHIP'
|
||||
) AS DELV_WBS_CNT,
|
||||
-- 납품진척율: Level1 PROGRESS 평균 (계층별 집계 결과)
|
||||
CASE
|
||||
WHEN (SELECT COUNT(1) FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'SHIP' AND O.TASK_LEVEL = '1') = 0 THEN 0
|
||||
ELSE ROUND(
|
||||
(SELECT COALESCE(AVG(CASE WHEN O.PROGRESS IS NOT NULL AND O.PROGRESS != ''
|
||||
THEN CAST(O.PROGRESS AS numeric) ELSE 0 END), 0)
|
||||
FROM PMS_WBS_TASK AS O
|
||||
WHERE O.CONTRACT_OBJID = PM.OBJID
|
||||
AND O.WBS_TYPE = 'SHIP' AND O.TASK_LEVEL = '1')
|
||||
, 1)
|
||||
END AS DELV_PROGRESS_RATE,
|
||||
PM.REGDATE AS SORT_DATE
|
||||
FROM
|
||||
PROJECT_MGMT PM
|
||||
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
LEFT OUTER JOIN CONTRACT_ITEM CI ON PM.CONTRACT_OBJID = CI.CONTRACT_OBJID
|
||||
AND PM.PART_OBJID = CI.PART_OBJID
|
||||
AND CI.STATUS = 'ACTIVE'
|
||||
LEFT OUTER JOIN PRODUCTION_PLAN PP ON PP.PROJECT_OBJID = PM.OBJID
|
||||
AND PP.STATUS = 'active'
|
||||
WHERE PM.PROJECT_NO IS NOT NULL AND PM.PROJECT_NO != ''
|
||||
AND PM.PRODUCT = '0000928'
|
||||
) T
|
||||
WHERE 1=1
|
||||
<if test="search_project_nos != null and search_project_nos != ''">
|
||||
AND T.OBJID::VARCHAR IN
|
||||
<foreach item="projNo" collection="search_project_nos.split(',')" open="(" separator="," close=")">
|
||||
#{projNo}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="search_product_code != null and search_product_code != ''">
|
||||
AND T.PRODUCT = #{search_product_code}
|
||||
</if>
|
||||
<if test="search_category_code != null and search_category_code != ''">
|
||||
AND T.CATEGORY_CODE = #{search_category_code}
|
||||
</if>
|
||||
<if test="search_production_type != null and search_production_type != ''">
|
||||
AND T.PRODUCTION_TYPE = #{search_production_type}
|
||||
</if>
|
||||
<if test="search_customer_objid != null and search_customer_objid != ''">
|
||||
AND (
|
||||
T.CUSTOMER_OBJID = #{search_customer_objid}
|
||||
OR T.CUSTOMER_OBJID = REPLACE(#{search_customer_objid}, 'C_', '')
|
||||
OR REPLACE(T.CUSTOMER_OBJID, 'C_', '') = REPLACE(#{search_customer_objid}, 'C_', '')
|
||||
)
|
||||
</if>
|
||||
<if test="search_req_del_date_from != null and search_req_del_date_from != ''">
|
||||
AND T.REQ_DEL_DATE >= #{search_req_del_date_from}
|
||||
</if>
|
||||
<if test="search_req_del_date_to != null and search_req_del_date_to != ''">
|
||||
AND T.REQ_DEL_DATE <= #{search_req_del_date_to}
|
||||
</if>
|
||||
<if test="search_part_no != null and search_part_no != ''">
|
||||
AND UPPER(T.PART_NO) LIKE '%' || UPPER(#{search_part_no}) || '%'
|
||||
</if>
|
||||
<if test="search_part_name != null and search_part_name != ''">
|
||||
AND UPPER(T.PART_NAME) LIKE '%' || UPPER(#{search_part_name}) || '%'
|
||||
</if>
|
||||
<if test="search_serial_no != null and search_serial_no != ''">
|
||||
AND UPPER(T.SERIAL_NO) LIKE '%' || UPPER(#{search_serial_no}) || '%'
|
||||
</if>
|
||||
ORDER BY T.SORT_DATE DESC, T.PROJECT_NO DESC
|
||||
</select>
|
||||
|
||||
<!-- 프로젝트 정보 조회 (생산계획 폼용) - PROJECT_MGMT 또는 PRODUCTION_PLAN에서 조회 -->
|
||||
<select id="getProdPlanProjectInfo" parameterType="map" resultType="map">
|
||||
|
||||
@@ -2415,4 +2415,45 @@ public class ProductionPlanningService {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 장비 WBS 할당 저장
|
||||
*/
|
||||
public Map saveEquipWbsAssign(Map<String, Object> paramMap){
|
||||
Map resultMap = new HashMap();
|
||||
SqlSession sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
|
||||
try{
|
||||
String projectObjid = (String)paramMap.get("projectObjid");
|
||||
String wbsType = (String)paramMap.get("wbsType");
|
||||
String writer = (String)paramMap.get("writer");
|
||||
List<Map<String, Object>> tasks = (List<Map<String, Object>>)paramMap.get("tasks");
|
||||
|
||||
// 기존 WBS 태스크 삭제
|
||||
Map deleteParam = new HashMap();
|
||||
deleteParam.put("projectObjid", projectObjid);
|
||||
deleteParam.put("wbsType", wbsType);
|
||||
sqlSession.delete("productionplanning.deleteEquipWbsTasks", deleteParam);
|
||||
|
||||
// 태스크 INSERT
|
||||
for(int i=0; i<tasks.size(); i++){
|
||||
Map<String, Object> task = tasks.get(i);
|
||||
task.put("projectObjid", projectObjid);
|
||||
task.put("wbsType", wbsType);
|
||||
task.put("writer", writer);
|
||||
task.put("newObjid", CommonUtils.createObjId());
|
||||
sqlSession.insert("productionplanning.insertEquipWbsTask", task);
|
||||
}
|
||||
|
||||
sqlSession.commit();
|
||||
resultMap.put("success", true);
|
||||
}catch(Exception e){
|
||||
sqlSession.rollback();
|
||||
resultMap.put("success", false);
|
||||
resultMap.put("message", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}finally{
|
||||
if(sqlSession != null) sqlSession.close();
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user