528 lines
13 KiB
Plaintext
528 lines
13 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
|
|
<%@ 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>
|
|
<script type="text/javascript" src="/js/tabulator/tabulator_custom.js"></script>
|
|
<style>
|
|
body, html {
|
|
margin: 0;
|
|
padding: 0;
|
|
height: 100%;
|
|
overflow: hidden;
|
|
}
|
|
.container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
}
|
|
.header {
|
|
padding: 15px 20px;
|
|
background: #f5f5f5;
|
|
border-bottom: 2px solid #ddd;
|
|
}
|
|
.header h3 {
|
|
margin: 0 0 10px 0;
|
|
color: #333;
|
|
}
|
|
.info-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
background: #fff;
|
|
border: 1px solid #ddd;
|
|
}
|
|
.info-table td {
|
|
padding: 8px 15px;
|
|
border: 1px solid #eee;
|
|
}
|
|
.info-table label {
|
|
font-weight: bold;
|
|
color: #555;
|
|
margin-right: 8px;
|
|
}
|
|
.info-table span {
|
|
color: #333;
|
|
}
|
|
.content {
|
|
flex: 1;
|
|
padding: 20px;
|
|
overflow: auto;
|
|
}
|
|
.status-badge {
|
|
display: inline-block;
|
|
padding: 3px 10px;
|
|
border-radius: 3px;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
}
|
|
.status-create { background: #e3f2fd; color: #1976d2; }
|
|
.status-sent { background: #fff3e0; color: #f57c00; }
|
|
.status-received { background: #e8f5e9; color: #388e3c; }
|
|
.status-completed { background: #f3e5f5; color: #7b1fa2; }
|
|
.vendor-type-badge {
|
|
display: inline-block;
|
|
padding: 2px 8px;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
}
|
|
.vendor-supply { background: #e3f2fd; color: #1976d2; }
|
|
.vendor-processing { background: #e8f5e9; color: #388e3c; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<div style="float: left;">
|
|
<h3 style="margin: 0;">
|
|
견적요청서
|
|
<c:if test="${not empty resultMap.QUOTATION_REQUEST_NO}">
|
|
- ${resultMap.QUOTATION_REQUEST_NO}
|
|
</c:if>
|
|
<c:choose>
|
|
<c:when test="${resultMap.STATUS eq 'create'}">
|
|
<span class="status-badge status-create">작성중</span>
|
|
</c:when>
|
|
<c:when test="${resultMap.STATUS eq 'sent'}">
|
|
<span class="status-badge status-sent">발송완료</span>
|
|
</c:when>
|
|
<c:when test="${resultMap.STATUS eq 'received'}">
|
|
<span class="status-badge status-received">견적수신</span>
|
|
</c:when>
|
|
<c:when test="${resultMap.STATUS eq 'completed'}">
|
|
<span class="status-badge status-completed">완료</span>
|
|
</c:when>
|
|
</c:choose>
|
|
</h3>
|
|
</div>
|
|
<div style="float: right;">
|
|
<input type="button" value="저장" class="plm_btns" onclick="fn_save();" style="margin-right: 5px;">
|
|
<input type="button" value="닫기" class="plm_btns" onclick="window.close();">
|
|
</div>
|
|
<div style="clear: both;"></div>
|
|
|
|
<!-- 프로젝트/업체 정보 -->
|
|
<table class="info-table" style="margin-top: 15px;">
|
|
<tr>
|
|
<td style="width:25%;">
|
|
<label>프로젝트번호:</label>
|
|
<span id="infoProjectNo">${resultMap.PROJECT_NO}</span>
|
|
</td>
|
|
<td style="width:25%;">
|
|
<label>구매유형:</label>
|
|
<span id="infoPurchaseType">${resultMap.PURCHASE_TYPE_NAME}</span>
|
|
</td>
|
|
<td style="width:25%;">
|
|
<label>주문유형:</label>
|
|
<span id="infoOrderType">${resultMap.ORDER_TYPE_NAME}</span>
|
|
</td>
|
|
<td style="width:25%;">
|
|
<label>제품구분:</label>
|
|
<span id="infoProductName">${resultMap.PRODUCT_NAME_TITLE}</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<td>
|
|
<label>요청번호:</label>
|
|
<span id="infoRequestNo">${resultMap.REQUEST_MNG_NO}</span>
|
|
</td>
|
|
<td colspan="2">
|
|
<label>업체명:</label>
|
|
<span id="infoVendorName" style="font-weight: bold; color: #1976d2;">${resultMap.VENDOR_NAME}</span>
|
|
<c:choose>
|
|
<c:when test="${resultMap.VENDOR_TYPE eq 'SUPPLY'}">
|
|
<span class="vendor-type-badge vendor-supply">공급업체</span>
|
|
</c:when>
|
|
<c:when test="${resultMap.VENDOR_TYPE eq 'PROCESSING'}">
|
|
<span class="vendor-type-badge vendor-processing">가공업체</span>
|
|
</c:when>
|
|
</c:choose>
|
|
</td>
|
|
<td>
|
|
<label>업체 이메일:</label>
|
|
<span id="infoVendorEmail">${resultMap.VENDOR_EMAIL}</span>
|
|
</td>
|
|
</tr>
|
|
<c:if test="${not empty resultMap.MAIL_SEND_DATE_TITLE}">
|
|
<tr>
|
|
<td colspan="4">
|
|
<label>메일발송일:</label>
|
|
<span id="infoMailSendDate" style="color: green;">${resultMap.MAIL_SEND_DATE_TITLE}</span>
|
|
</td>
|
|
</tr>
|
|
</c:if>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="content">
|
|
<div style="margin-bottom: 20px;">
|
|
<%-- 업체유형 설명 주석처리
|
|
<c:choose>
|
|
<c:when test="${resultMap.VENDOR_TYPE eq 'SUPPLY'}">
|
|
<span style="font-weight: bold; color: #1976d2;">공급업체 견적 - 품번/품명/제작수량 기준</span>
|
|
</c:when>
|
|
<c:when test="${resultMap.VENDOR_TYPE eq 'PROCESSING'}">
|
|
<span style="font-weight: bold; color: #388e3c;">가공업체 견적 - 소재품번/소재재질/규격/발주수량 기준</span>
|
|
</c:when>
|
|
</c:choose>
|
|
--%>
|
|
<span style="float: right; color: #666; font-size: 12px;">
|
|
* 단가 입력 후 저장하면 구매리스트에 자동 반영됩니다.
|
|
</span>
|
|
</div>
|
|
<div id="quotationDetailTable"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var _tabulGrid;
|
|
var quotationRequestMasterObjid = "${resultMap.OBJID}";
|
|
var vendorType = "${resultMap.VENDOR_TYPE}";
|
|
var currentStatus = "${resultMap.STATUS}";
|
|
|
|
$(document).ready(function(){
|
|
fn_initGrid();
|
|
fn_loadDetailList();
|
|
});
|
|
|
|
// 그리드 초기화
|
|
function fn_initGrid() {
|
|
var columns = [];
|
|
|
|
// 공통 컬럼
|
|
columns.push({title:'OBJID', field:'OBJID', visible:false});
|
|
columns.push({title:'SALES_REQUEST_PART_OBJID', field:'SALES_REQUEST_PART_OBJID', visible:false});
|
|
columns.push({title:'PART_OBJID', field:'PART_OBJID', visible:false});
|
|
|
|
// No
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'center',
|
|
width: 50,
|
|
title: 'No',
|
|
field: 'ROW_NUM',
|
|
formatter: "rownum"
|
|
});
|
|
|
|
// 업체유형에 따라 다른 컬럼 표시
|
|
if(vendorType === 'PROCESSING') {
|
|
// 가공업체: 품번, 품명, 규격(없음), 제작수량
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
widthGrow: 2,
|
|
title: '품번',
|
|
field: 'PART_NO'
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
widthGrow: 3,
|
|
title: '품명',
|
|
field: 'PART_NAME'
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'center',
|
|
width: 100,
|
|
title: '규격',
|
|
field: 'SIZE',
|
|
formatter: function(cell) {
|
|
return '-';
|
|
}
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'right',
|
|
width: 100,
|
|
title: '제작수량',
|
|
field: 'QTY',
|
|
formatter: function(cell) {
|
|
var value = cell.getValue();
|
|
return value ? Number(value).toLocaleString() : '0';
|
|
}
|
|
});
|
|
} else {
|
|
// 공급업체: 소재품번, 소재재질, 규격, 발주수량
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
widthGrow: 2,
|
|
title: '소재품번',
|
|
field: 'PART_NO'
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
widthGrow: 2,
|
|
title: '소재재질',
|
|
field: 'RAW_MATERIAL'
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
width: 120,
|
|
title: '규격',
|
|
field: 'SIZE'
|
|
});
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'right',
|
|
width: 100,
|
|
title: '발주수량',
|
|
field: 'QTY',
|
|
formatter: function(cell) {
|
|
var value = cell.getValue();
|
|
return value ? Number(value).toLocaleString() : '0';
|
|
}
|
|
});
|
|
}
|
|
|
|
// 업체명
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'left',
|
|
widthGrow: 1.5,
|
|
title: vendorType === 'PROCESSING' ? '가공업체' : '공급업체',
|
|
field: 'VENDOR_NAME'
|
|
});
|
|
|
|
// 환종
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'center',
|
|
width: 70,
|
|
title: '환종',
|
|
field: 'CURRENCY_NAME'
|
|
});
|
|
|
|
// 단가 (수정가능 - 클릭 시 전체 선택)
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'right',
|
|
width: 120,
|
|
title: '<span style="background-color: #FFFF00; padding: 2px 5px;">단가</span>',
|
|
field: 'UNIT_PRICE',
|
|
editor: function(cell, onRendered, success, cancel) {
|
|
var input = document.createElement("input");
|
|
input.type = "number";
|
|
input.style.width = "100%";
|
|
input.style.boxSizing = "border-box";
|
|
input.style.textAlign = "right";
|
|
input.value = cell.getValue() || '';
|
|
|
|
onRendered(function() {
|
|
input.focus();
|
|
input.select();
|
|
});
|
|
|
|
function onComplete() {
|
|
var val = input.value;
|
|
success(val !== '' ? Number(val) : 0);
|
|
}
|
|
input.addEventListener("blur", onComplete);
|
|
input.addEventListener("keydown", function(e) {
|
|
if(e.keyCode === 13) onComplete();
|
|
if(e.keyCode === 27) cancel();
|
|
});
|
|
return input;
|
|
},
|
|
editable: true,
|
|
formatter: function(cell) {
|
|
var value = cell.getValue();
|
|
return value ? Number(value).toLocaleString() : '0';
|
|
}
|
|
});
|
|
|
|
// 입고요청일 (수정가능 - datepicker + 복사/붙여넣기 지원)
|
|
columns.push({
|
|
headerHozAlign: 'center',
|
|
hozAlign: 'center',
|
|
width: 130,
|
|
title: '<span style="background-color: #FFFF00; padding: 2px 5px;">입고요청일</span>',
|
|
field: 'DELIVERY_REQUEST_DATE',
|
|
editor: function(cell, onRendered, success, cancel) {
|
|
var input = document.createElement("input");
|
|
input.type = "text";
|
|
input.style.width = "100%";
|
|
input.style.boxSizing = "border-box";
|
|
input.style.textAlign = "center";
|
|
input.value = cell.getValue() || '';
|
|
var isCompleted = false;
|
|
|
|
function onComplete(val) {
|
|
if(isCompleted) return;
|
|
isCompleted = true;
|
|
try { $(input).datepicker("hide"); $(input).datepicker("destroy"); } catch(e) {}
|
|
success(val);
|
|
}
|
|
|
|
onRendered(function() {
|
|
$(input).datepicker({
|
|
dateFormat: 'yy-mm-dd',
|
|
changeMonth: true,
|
|
changeYear: true,
|
|
onSelect: function(dateText) {
|
|
onComplete(dateText);
|
|
},
|
|
onClose: function() {
|
|
onComplete(input.value);
|
|
}
|
|
});
|
|
input.focus();
|
|
input.select();
|
|
});
|
|
|
|
input.addEventListener("keydown", function(e) {
|
|
if(e.keyCode === 13) { e.preventDefault(); onComplete(input.value); }
|
|
if(e.keyCode === 27) cancel();
|
|
});
|
|
return input;
|
|
},
|
|
editable: true
|
|
});
|
|
|
|
// // 총단가 (자동계산) - 주석처리
|
|
// columns.push({
|
|
// headerHozAlign: 'center',
|
|
// hozAlign: 'right',
|
|
// width: 120,
|
|
// title: '총단가',
|
|
// field: 'TOTAL_PRICE',
|
|
// formatter: function(cell) {
|
|
// var data = cell.getRow().getData();
|
|
// var qty = parseFloat(data.QTY) || 0;
|
|
// var unitPrice = parseFloat(data.UNIT_PRICE) || 0;
|
|
// var totalPrice = qty * unitPrice;
|
|
// return totalPrice > 0 ? totalPrice.toLocaleString() : '0';
|
|
// }
|
|
// });
|
|
|
|
// // 비고 - 주석처리
|
|
// columns.push({
|
|
// headerHozAlign: 'center',
|
|
// hozAlign: 'left',
|
|
// widthGrow: 2,
|
|
// title: '비고',
|
|
// field: 'REMARK',
|
|
// editor: 'input',
|
|
// editable: true
|
|
// });
|
|
|
|
_tabulGrid = new Tabulator("#quotationDetailTable", {
|
|
layout: "fitColumns",
|
|
height: "calc(100vh - 300px)",
|
|
columns: columns,
|
|
data: [],
|
|
placeholder: "데이터가 없습니다."
|
|
});
|
|
|
|
// 셀 편집 이벤트 (총단가 자동 계산)
|
|
_tabulGrid.on("cellEdited", function(cell) {
|
|
var field = cell.getField();
|
|
var row = cell.getRow();
|
|
|
|
if(field === 'UNIT_PRICE') {
|
|
row.reformat();
|
|
}
|
|
});
|
|
}
|
|
|
|
// 상세 목록 조회
|
|
function fn_loadDetailList() {
|
|
if(!quotationRequestMasterObjid || quotationRequestMasterObjid === '') {
|
|
return;
|
|
}
|
|
|
|
$.ajax({
|
|
url: "/salesMng/getQuotationRequestDetailList.do",
|
|
method: 'post',
|
|
data: {
|
|
QUOTATION_REQUEST_MASTER_OBJID: quotationRequestMasterObjid
|
|
},
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
if(data && data.list) {
|
|
_tabulGrid.setData(data.list);
|
|
}
|
|
},
|
|
error: function(jqxhr, status, error){
|
|
console.error("상세 목록 조회 오류:", error);
|
|
Swal.fire({
|
|
title: '오류',
|
|
text: '상세 목록 조회 중 오류가 발생했습니다.',
|
|
icon: 'error'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// 저장
|
|
function fn_save() {
|
|
var gridData = _tabulGrid.getData();
|
|
|
|
if(!gridData || gridData.length === 0) {
|
|
Swal.fire({
|
|
title: '알림',
|
|
text: '저장할 데이터가 없습니다.',
|
|
icon: 'warning'
|
|
});
|
|
return;
|
|
}
|
|
|
|
Swal.fire({
|
|
title: '확인',
|
|
text: '저장하시겠습니까?\n단가가 구매리스트에 자동 반영됩니다.',
|
|
icon: 'question',
|
|
showCancelButton: true,
|
|
confirmButtonText: '확인',
|
|
cancelButtonText: '취소'
|
|
}).then((result) => {
|
|
if (result.isConfirmed) {
|
|
$.ajax({
|
|
url: "/salesMng/saveQuotationRequestPrice.do",
|
|
method: 'post',
|
|
data: {
|
|
QUOTATION_REQUEST_MASTER_OBJID: quotationRequestMasterObjid,
|
|
DETAIL_LIST: JSON.stringify(gridData)
|
|
},
|
|
dataType: 'json',
|
|
success: function(data) {
|
|
if(data && (data.resultFlag === 'S' || data.RESULTFLAG === 'S')) {
|
|
Swal.fire({
|
|
title: '성공',
|
|
text: data.message || '저장되었습니다.',
|
|
icon: 'success'
|
|
}).then(() => {
|
|
if(opener && opener.fn_search) {
|
|
opener.fn_search();
|
|
}
|
|
});
|
|
} else {
|
|
Swal.fire({
|
|
title: '실패',
|
|
text: data.message || data.MESSAGE || '저장에 실패했습니다.',
|
|
icon: 'error'
|
|
});
|
|
}
|
|
},
|
|
error: function(jqxhr, status, error){
|
|
console.error("저장 오류:", error);
|
|
Swal.fire({
|
|
title: '오류',
|
|
text: '저장 중 오류가 발생했습니다.',
|
|
icon: 'error'
|
|
});
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|