feat: 견적서 작성 기능 추가 (일반 견적서, 장비 견적서)

- 견적서 템플릿 2종 추가 (estimateTemplate1.jsp, estimateTemplate2.jsp)
- 견적서 작성 팝업 기능 구현 (estimateList_new.jsp)
- 견적서 템플릿 컨트롤러 및 서비스 메서드 추가
- 견적서 템플릿용 DB 테이블 스키마 생성 스크립트 추가
- 회사 직인 이미지 추가
- 견적서 양식: A4 인쇄 최적화, 동적 품목 추가/수정, 자동 금액 계산
This commit is contained in:
2025-10-15 15:45:34 +09:00
parent c3c86ae9cf
commit 60356f886c
8 changed files with 2409 additions and 0 deletions

View File

@@ -0,0 +1,591 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ page import="java.util.*" %>
<%@include file= "/init.jsp" %>
<%
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
String connector = person.getUserId();
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%></title>
<c:set var="now" value="<%=new java.util.Date() %>"/>
<c:set var="sysYear"><fmt:formatDate value="${now}" pattern="yyyy" /></c:set>
<c:set var="connector" value="<%=connector %>" />
<script>
$(document).ready(function(){
//날짜
_fnc_datepick();
$('.select2').select2();
$("#btnSearch").click(function(){
$("#page").val("1");
fn_search();
});
//SR자료등록 팝업
$(".File").click(function(){
var popup_width = 800;
var popup_height = 335;
var objId = $(this).attr("data-OBJID");
var docType =$(this).attr("data-docType");
var docTypeName = $(this).attr("data-docTypeName");
var params = "?targetObjId="+objId+"&docType="+docType+"&docTypeName="+docTypeName;
var url = "/projectConcept/FileRegistPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
});
//영업활동 등록 팝업
$(".btnRegist").click(function(){
var popup_width = 650;
var popup_height = 400;
var params = "?actionType=regist"
var url = "/contractMgmt/estimateRegistFormPopup.do"+params;
//window.open("/ordermgmt/ordermgmtUpdateFormPopup.do"+params, "", "width=650, height=750","menubars=no, scrollbars=yes, resizable=yes");
fn_centerPopup(popup_width, popup_height, url);
});
//견적작성 팝업
$("#btnEstimate").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length < 1){
Swal.fire("견적서를 작성할 행을 선택해주십시오.");
return false;
} else if(selectedData.length > 1){
Swal.fire("한번에 한개의 견적서만 작성 가능합니다.");
return false;
} else {
var objId = fnc_checkNull(selectedData[0].OBJID);
// 견적서 양식 선택 팝업
Swal.fire({
title: '견적서 양식을 선택하세요',
icon: 'question',
showCancelButton: true,
showDenyButton: true,
confirmButtonText: '일반 견적서',
denyButtonText: '장비 견적서',
cancelButtonText: '취소',
confirmButtonColor: '#3085d6',
denyButtonColor: '#28a745',
cancelButtonColor: '#d33',
}).then((result) => {
if (result.isConfirmed) {
// 일반 견적서 (Template 1)
fn_openEstimateTemplate(objId, "1");
} else if (result.isDenied) {
// 장비 견적서 (Template 2)
fn_openEstimateTemplate(objId, "2");
}
});
}
});
//고객 등록 팝업
$(".supplyMng").click(function(){
//window.open("/ordermgmt/ordermgmtUpdateFormPopup.do"+params, "", "width=650, height=750","menubars=no, scrollbars=yes, resizable=yes");
window.open("/contractMgmt/supplyMngPagingList.do", "", "width=1150, height=676","menubars=no, scrollbars=yes, resizable=yes");
});
//엔터 조회
$("input").keyup(function(e){
if(e.keyCode == 13){
$("#page").val("1");
fn_search();
}
});
//삭제
$("#btnDelete").click(function(){
fn_delete();
});
$("#btnExcel").click(function(){
document.form1.actionType.value = "excel";
document.form1.action = "/contractMgmt/contractList.do";
document.form1.submit();
});
//결재상신
$("#btnApproval").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length<1){
Swal.fire("결재상신할 행을 선택해주십시오.");
return false;
}else if(selectedData.length>1){
Swal.fire("한번에 한개의 결재만 가능합니다.");
return false;
}else{
var targetStatus = fnc_checkNull(selectedData[0].APPR_STATUS);
var status = fnc_checkNull(selectedData[0].STATUS);
if(targetStatus == "결재완료" || targetStatus == "결재중" || status == "cancel"){
Swal.fire("작성중/결재반려인 상태만 결재상신 가능합니다.");
return false;
}else{
if(confirm("결재상신 하시겠습니까?")){
var objId = fnc_checkNull(selectedData[0].OBJID);
var title = encodeURIComponent(fnc_checkNull(selectedData[0].CONTRACT_NO));
window.open("/approval/registApproval.do?targetType=CONTRACT_ESTIMATE&targetObjId="+objId+"&approvalTitle="+title,"registApproval","width=700,height=700");
}
}
}
});
fn_search();
});
var columns = [
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '영업번호', field : 'CONTRACT_NO', frozen:true,
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var objid = fnc_checkNull(cell.getData().OBJID);
fn_projectConceptDetail(objid);
}
},
{title:"영업정보(상세)", headerHozAlign:'center', //고객정보
columns:[
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '계약구분', field : 'CATEGORY_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '차수', field : 'OVERHAUL_ORDER'},
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '국내/해외', field : 'AREA_NAME' },
{headerHozAlign : 'center', hozAlign : 'left', width : '150', title : '고객사', field : 'CUSTOMER_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '제품구분', field : 'PRODUCT_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '기계형식', field : 'MECHANICAL_TYPE' },
{headerHozAlign : 'center', hozAlign : 'left', width : '200', title : '고객사 프로젝트명', field : 'CUSTOMER_PROJECT_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '예상납기일', field : 'DUE_DATE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '입고지', field : 'LOCATION' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '셋업지', field : 'SETUP' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '설비방향', field : 'FACILITY_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '설비대수', field : 'FACILITY_QTY' },
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '설비타입', field : 'FACILITY_TYPE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '설비길이', field : 'FACILITY_DEPTH' },
{headerHozAlign : 'center', hozAlign : 'center', width : '70', title : '담당자', field : 'WRITER_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '등록일', field : 'REG_DATE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '첨부파일', field : 'CU01_CNT',
formatter:fnc_subInfoValueFormatter,
cellClick:function(e, cell){
var objid = fnc_checkNull(cell.getData().OBJID);
var docType = 'contractMgmt01';
var docTypeName = 'contractMgmt01';
fn_FileRegist(objid, docType, docTypeName);
}
}
]
},
{title:"진행사항", headerHozAlign:'center',
columns:[
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '검토내용', field : 'CU03_CNT',
formatter:fnc_subInfoValueFormatter,
cellClick:function(e, cell){
var objid = fnc_checkNull(cell.getData().OBJID);
fn_projectConceptReviewDetail(objid);
}
},
{headerHozAlign : 'center', hozAlign : 'center', width : '70', title : '상태', field : 'CONTRACT_RESULT_NAME' }
]
},
{title:"수주정보", headerHozAlign:'center',
columns:[
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '수주일', field : 'CONTRACT_DATE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : 'PO계약 No', field : 'PO_NO' },
{headerHozAlign : 'center', hozAlign : 'center', width : '70', title : 'PM', field : 'PM_USER_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '70', title : '통화', field : 'CONTRACT_CURRENCY_NAME' },
{headerHozAlign : 'center', hozAlign : 'right', width : '100', title : '수주가', field : 'CONTRACT_PRICE_CURRENCY',
formatter:"money", formatterParams:{thousand:",", symbolAfter:"p", precision:false,},
},
{headerHozAlign : 'center', hozAlign : 'left', width : '170', title : '당사프로젝트명', field : 'PROJECT_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '계약납기일', field : 'CONTRACT_DEL_DATE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '요청납기일', field : 'REQ_DEL_DATE' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '수주회사', field : 'CONTRACT_COMPANY_NAME' },
{headerHozAlign : 'center', hozAlign : 'center', width : '80', title : '제작공장', field : 'MANUFACTURE_PLANT_NAME' }
]
},
{headerHozAlign : 'center', hozAlign : 'center', width : '100', title : '상태', field : 'APPR_STATUS' ,
formatter:fnc_createGridAnchorTag,
cellClick:function(e, cell){
var APPROVAL_OBJID = fnc_checkNull(cell.getData().APPROVAL_OBJID);
var ROUTE_OBJID = fnc_checkNull(cell.getData().ROUTE_OBJID);
approval_form(APPROVAL_OBJID,ROUTE_OBJID);
}
},
];
//var grid;
function fn_search(){
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/contractMgmt/contractGridList.do", columns, true);
}
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
});
}
}
function fn_delete(){
var checkedObj = _tabulGrid.getSelectedData();
if(0 < checkedObj.length){
var objId = fnc_checkNull(checkedObj[0].OBJID);
//if(confirm("선택한 정보를 삭제하시겠습니까?")){
Swal.fire({
title: '선택한 고객정보를 삭제하시겠습니까?',
text: '',
icon: 'warning',
showCancelButton: true, // cancel버튼 보이기. 기본은 원래 없음
confirmButtonColor: '#3085d6', // confrim 버튼 색깔 지정
cancelButtonColor: '#d33', // cancel 버튼 색깔 지정
confirmButtonText: '확인', // confirm 버튼 텍스트 지정
cancelButtonText: '취소', // cancel 버튼 텍스트 지정
reverseButtons: false, // 버튼 순서 거꾸로
}).then(result => {
// 만약 Promise리턴을 받으면,
if (result.isConfirmed) { // 만약 모달창에서 confirm 버튼을 눌렀다면
//var param = $("#form1").serialize();
$.ajax({
type : "POST",
url : "/contractMgmt/deleteContractMngInfo.do",
data: {
"objId":objId
},
dataType:"json",
success:function(data){
if(data.result == 'true'){
Swal.fire("삭제되었습니다.");
fn_search();
};
}
,error: function(jqxhr, status, error){
}
});
}
});
}else{
Swal.fire("선택한 항목이 없습니다.");
}
}
function approval_form(APPROVAL_OBJID,ROUTE_OBJID){
url = "/approval/approvalDetail.do?approvalObjId="+APPROVAL_OBJID+"&routeObjId="+ROUTE_OBJID;
fn_centerPopup(650,400,url,'approvalDetailPopup')
}
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_projectConceptDetail(objId){
var popup_width = 650;
var popup_height = 400;
var url = "/contractMgmt/estimateRegistFormPopup.do?objId="+objId;
fn_centerPopup(popup_width, popup_height, url);
}
//검토내용 상세
function fn_projectConceptReviewDetail(objId){
var popup_width = 800;
var popup_height = 250;
var url = "/contractMgmt/contracMgmtReviewFormPopup.do?objId="+objId;
fn_centerPopup(popup_width, popup_height, url);
}
//견적서 양식 열기
function fn_openEstimateTemplate(objId, templateType){
var popup_width = 900;
var popup_height = 800;
var url = "";
if(templateType === "1"){
// 일반 견적서
url = "/contractMgmt/estimateTemplate1.do?objId="+objId;
} else if(templateType === "2"){
// 장비 견적서
url = "/contractMgmt/estimateTemplate2.do?objId="+objId;
}
window.open(url, "estimateTemplate", "width="+popup_width+",height="+popup_height+",menubar=no,scrollbars=yes,resizable=yes");
}
//코드값을 받아와서 동적으로 selectbox 생성
function optionJobGroup(code){
var val=code;
var params = "";
var option="";
var combobox = $("#writer");
combobox.empty();
if(val!=""){
params += "codeId="+val;
$.ajax({
type : "POST",
url : "/projectConcept/makewrite.do",
data : params,
dataType:"json",
async: false,
success: function(data){
if(data.RESULT !=""){
option += "<option value=''>전체</option>";
option += data.RESULT;
combobox.append(option);
}
}
});
// alert(Number(lev-1));
//$("#code"+Number(lev-1)).val(code).prop("selected", true);
}else{
option += "<option value=''>전체</option>";
combobox.append(option);
}
}
//코드값을 받아와서 동적으로 selectbox 생성
function makeSelect(code){
var val=code;
var params = "";
var option="";
var combobox = $("#outc_cd6_td");
combobox.empty();
/* <select name="outc_cd6" id="outc_cd6" type="select">
<option value="">선택</option>
</select> */
if(val!="" && val!="PLA03000"){
params += "codeId="+val;
$.ajax({
type : "POST",
url : "/ordermgmt/makeCodeselect.do",
data : params,
dataType:"json",
async: false,
success: function(data){
if(data.RESULT !=""){
option += "<select name='outc_cd6' id='outc_cd6' type='select'>"
option += "<option value=''>선택</option>";
option += data.RESULT;
option += "</select>";
combobox.append(option);
$("#outc_cd6").val("${param.outc_cd6}");
//$("#outc_cd6").select2();
}
}
});
}else{
if(val=="PLA03000"){
option += "<input type='text' name='outc_cd6' id='outc_cd6' value='${param.outc_cd6}'>"
combobox.append(option);
}else{
option += "<select name='outc_cd6' id='outc_cd6' type='select'>"
option += "<option value=''>선택</option>";
option += "</select>";
combobox.append(option);
//$("#outc_cd6").select2();
}
}
}
function saveexcelpop() {
var partLength = $("input[name=partCheckBox]:checked").length;
if(partLength == 0){
Swal.fire('선택된 영업정보가 없습니다.');
return;
}
if(partLength > 1){
Swal.fire('단건씩 선택해 주세요');
return;
}
var orderobjId ="";
$("input[name=partCheckBox]:checked").each(function(){
orderobjId = fnc_checkNull($(this).attr("data-objId").replace(" ",""));
});
var url = "/ordermgmt/openOrderExcelImportPopUp.do?orderobjId="+orderobjId;
var target = "openOrderExcelImportPopUp";
window.open(url, target,"width=1245, height=700, menubars=no, scrollbars=yes, resizable=yes");
}
//프로젝트의 form,detail 팝업을 보여준다.
function openProjectFormPopUp(objId){
window.open("","projectFormPopUp","width=1150, height=676");
var params = "?actionType=regist"
var url = "/project/projectFormPopUp.do"+params;
if("" != objId){
url = "/project/projectDetailPopUp.do";
}
var form = document.form1;
form.objId.value = objId;
form.action = url;
form.target = "projectFormPopUp";
form.submit();
}
</script>
</head>
<body class="bodyNoScroll">
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="actionType" value="" />
<!-- 대시보드용 -->
<input type="hidden" name="contract_month" id="contract_month" value="${param.contract_month}">
<div class="min_part_enroll">
<div class="content-box">
<div class="content-box-s">
<div class="plm_menu_name_gdnsi">
<h2>
<span>영업관리_견적관리</span>
</h2>
<div class="btnArea">
<input type="button" value="조회" class="plm_btns" id="btnSearch" name="btnSearch">
<input type="button" value="삭제" class="plm_btns" id="btnDelete">
<input type="button" value="견적요청등록" class="plm_btns btnRegist">
<input type="button" value="결재상신" class="plm_btns" id="btnApproval">
<input type="button" value="견적작성" class="plm_btns" id="btnEstimate">
<input type="button" value="메일발송" class="plm_btns" id="btnMail">
<!-- <input type="button" value="Excel Download" class="plm_btns" id="btnExcel">
<input type="button" value="고객등록" class="plm_btns supplyMng create"> -->
</div>
</div>
<div id="plmSearchZon">
<table>
<tr>
<td><label for="category_cd">주문유형</label></td>
<td>
<select name="category_cd" id="category_cd" style="width:150px" class="select2" autocomplete="off">
<option value="">선택</option>
${code_map.category_cd}
</select>
</td>
<td><label for="product">제품구분</label></td>
<td>
<select name="product" id="product" style="width:130px" class="select2" autocomplete="off">
<option value="">선택</option>
${code_map.product_cd}
</select>
</td>
<td><label for="area_cd">국내/해외</label></td>
<td>
<select name="area_cd" id="area_cd" style="width:130px" class="select2" autocomplete="off">
<option value="">선택</option>
<option value="0001220">국내</option>
<option value="0001221">해외</option>
</select>
</td>
<td><label for="customer_objid">고객사</label></td>
<td>
<select name="customer_objid" id="customer_objid" style="width:190px" class="select2" autocomplete="off">
<option value="">선택</option>
${code_map.customer_cd}
</select>
</td>
<td><label for="paid_type">유/무상</label></td>
<td>
<select name="paid_type" id="paid_type" style="width:130px" class="select2" autocomplete="off">
<option value="">선택</option>
<option value="paid">유상</option>
<option value="free">무상</option>
</select>
</td>
<td class="align_r">
<label for="" class="">품번</label>
</td>
<td>
<input type="text" name="search_partNo" id="search_partNo" value="${param.search_partNo}"/>
</td>
<td class="align_r">
<label for="" class="">품명</label>
</td>
<td colspan="3">
<input type="text" name="search_partName" id="search_partName" value="${param.search_partName}"/>
</td>
</tr>
<tr>
<td class="">
<label for="" class="">S/N</label>
</td>
<td colspan="">
<input type="text" name="search_serialNo" id="search_serialNo" value="${param.search_serialNo}"/>
</td>
<td><label for="">결재상태</label></td>
<td>
<select name="appr_status" id="appr_status" class="select2" autocomplete="off" style="width:130px">
<option value="">선택</option>
<%-- ${code_map.appr_status} --%>
<option value="작성중">작성중</option>
<option value="결재중">결재중</option>
<option value="반려">반려</option>
<option value="결재완료">결재완료</option>
<option value="취소">취소</option>
</select>
</td>
<td class="">
<label>접수일</label>
</td>
<td>
<input type="text" name="receipt_start_date" id="receipt_start_date" style="width:90px;" autocomplete="off" value="${param.receipt_start_date}" class="date_icon">~
<input type="text" name="receipt_end_date" id="receipt_end_date" style="width:90px;" autocomplete="off" value="${param.receipt_end_date}" class="date_icon">
</td>
<td class="">
<label>요청납기</label>
</td>
<td>
<input type="text" name="due_start_date" id="due_start_date" style="width:90px;" autocomplete="off" value="${param.due_start_date}" class="date_icon">~
<input type="text" name="due_end_date" id="due_end_date" style="width:90px;" autocomplete="off" value="${param.due_end_date}" class="date_icon">
</td>
</tr>
</table>
</div>
<%@include file= "/WEB-INF/view/common/common_gridArea.jsp" %>
</div>
</div>
</form>
</body>
</html>

View File

@@ -0,0 +1,552 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@include file= "/init.jsp" %>
<%
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
String userId = CommonUtils.checkNull(person.getUserId());
String objId = CommonUtils.checkNull(request.getParameter("objId"));
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%> - 견적서 양식1</title>
<style type="text/css">
@media print {
@page {
size: A4;
margin: 10mm;
}
body {
margin: 0;
padding: 0;
}
.no-print {
display: none !important;
}
}
body {
font-family: "Malgun Gothic", "맑은 고딕", Arial, sans-serif;
font-size: 12pt;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.estimate-container {
width: 210mm;
min-height: 297mm;
background: white;
margin: 0 auto;
padding: 20mm;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
box-sizing: border-box;
}
.header-section {
margin-bottom: 30px;
}
.title {
text-align: center;
font-size: 28pt;
font-weight: bold;
letter-spacing: 20px;
margin-bottom: 40px;
padding: 10px 0;
}
.info-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.info-table td {
padding: 5px 8px;
border: 1px solid #000;
font-size: 9pt;
}
.info-table .label {
background-color: #f0f0f0;
font-weight: bold;
width: 80px;
text-align: center;
}
.company-info {
float: right;
text-align: right;
margin-top: -80px;
margin-bottom: 20px;
}
.company-stamp {
width: 120px;
height: 120px;
border: 2px solid #e74c3c;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
margin-bottom: 10px;
position: relative;
}
.company-stamp-text {
writing-mode: vertical-rl;
font-size: 16pt;
font-weight: bold;
color: #e74c3c;
letter-spacing: 3px;
}
.company-details {
font-size: 9pt;
line-height: 1.6;
margin-top: 10px;
}
.greeting {
font-size: 11pt;
margin-bottom: 20px;
line-height: 1.8;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 30px;
}
.items-table th,
.items-table td {
border: 1px solid #000;
padding: 6px 8px;
text-align: center;
font-size: 9pt;
}
.items-table th {
background-color: #f0f0f0;
font-weight: bold;
}
.items-table .col-no { width: 8%; }
.items-table .col-desc { width: 22%; }
.items-table .col-spec { width: 25%; }
.items-table .col-qty { width: 8%; }
.items-table .col-unit { width: 10%; }
.items-table .col-price { width: 12%; }
.items-table .col-amount { width: 12%; }
.items-table .col-note { width: 13%; }
.items-table .text-left {
text-align: left;
}
.items-table .text-right {
text-align: right;
}
.notes-section {
margin-top: 30px;
font-size: 10pt;
line-height: 1.8;
}
.notes-title {
font-weight: bold;
margin-bottom: 10px;
}
.footer-company {
text-align: right;
margin-top: 40px;
font-size: 12pt;
font-weight: bold;
}
.btn-area {
text-align: right;
margin-top: 20px;
padding: 10px;
}
.plm_btns {
padding: 10px 30px;
margin: 0 5px;
font-size: 14px;
cursor: pointer;
border: 1px solid #ddd;
background-color: #f8f9fa;
transition: background-color 0.2s;
}
.plm_btns:hover {
background-color: #e2e6ea;
border-color: #dae0e5;
}
input[type="text"],
textarea {
border: none;
outline: none;
background: transparent;
width: 100%;
font-family: inherit;
font-size: inherit;
}
textarea {
resize: vertical;
min-height: 30px;
}
.editable {
background-color: #fffef0;
}
@media print {
.editable {
background-color: transparent;
}
}
</style>
<script type="text/javascript">
$(function(){
// 인쇄 버튼
$("#btnPrint").click(function(){
window.print();
});
// 저장 버튼
$("#btnSave").click(function(){
if(confirm("견적서를 저장하시겠습니까?")) {
fn_save();
}
});
// 닫기 버튼
$("#btnClose").click(function(){
self.close();
});
// 행 추가 버튼
$("#btnAddRow").click(function(){
fn_addItemRow();
});
// 금액 자동 계산
$(document).on("change keyup", ".item-qty, .item-price", function(){
fn_calculateAmount($(this).closest("tr"));
});
// 콤마 자동 추가
$(".item-price, .item-amount").on("blur", function(){
var val = $(this).val().replace(/,/g, "");
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
}
});
// 데이터 로드
if("<%=objId%>" !== "" && "<%=objId%>" !== "-1") {
fn_loadData();
}
});
// 금액 계산
function fn_calculateAmount(row) {
var qty = row.find(".item-qty").val().replace(/,/g, "") || "0";
var price = row.find(".item-price").val().replace(/,/g, "") || "0";
var amount = parseInt(qty) * parseInt(price);
if(!isNaN(amount)) {
row.find(".item-amount").val(addComma(amount));
}
}
// 콤마 추가
function addComma(num) {
var regexp = /\B(?=(\d{3})+(?!\d))/g;
return num.toString().replace(regexp, ',');
}
// 행 추가 함수
function fn_addItemRow() {
// 비고 행 제외하고 마지막 품목 행 찾기
var $lastRow = $("#itemsTableBody tr").not(":last");
var lastRowIndex = $lastRow.length;
var nextNo = lastRowIndex + 1;
// 새 행 생성
var newRow = '<tr>' +
'<td>' + nextNo + '</td>' +
'<td class="text-left editable"><input type="text" class="item-desc" value=""></td>' +
'<td class="text-left editable"><textarea class="item-spec"></textarea></td>' +
'<td class="editable"><input type="text" class="item-qty" value=""></td>' +
'<td class="editable"><input type="text" class="item-unit" value="EA"></td>' +
'<td class="text-right editable"><input type="text" class="item-price" value=""></td>' +
'<td class="text-right editable"><input type="text" class="item-amount" value="₩0" readonly></td>' +
'<td class="editable"><input type="text" class="item-note" value=""></td>' +
'</tr>';
// 비고 행 바로 위에 추가
$("#itemsTableBody tr:last").before(newRow);
// 콤마 자동 추가 이벤트 바인딩
$("#itemsTableBody").find(".item-price, .item-amount").last().on("blur", function(){
var val = $(this).val().replace(/,/g, "");
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
}
});
}
// 데이터 로드
function fn_loadData() {
$.ajax({
url: "/contractMgmt/getEstimateDetail.do",
type: "POST",
data: {
objId: "<%=objId%>"
},
dataType: "json",
success: function(data) {
if(data && data.estimate) {
// 데이터 바인딩
$("#executor").val(data.estimate.EXECUTOR || "");
$("#recipient").val(data.estimate.RECIPIENT || "");
$("#estimate_no").val(data.estimate.ESTIMATE_NO || "");
$("#contact_person").val(data.estimate.CONTACT_PERSON || "");
$("#greeting_text").val(data.estimate.GREETING_TEXT || "견적을 요청해 주셔서 대단히 감사합니다.\n하기와 같이 견적서를 제출합니다.");
// 품목 데이터 로드
if(data.items && data.items.length > 0) {
// 기존 행 초기화 후 데이터 추가
var itemsHtml = "";
for(var i = 0; i < data.items.length; i++) {
var item = data.items[i];
itemsHtml += '<tr>';
itemsHtml += '<td>' + (i + 1) + '</td>';
itemsHtml += '<td class="text-left editable"><input type="text" class="item-desc" value="' + (item.DESCRIPTION || '') + '"></td>';
itemsHtml += '<td class="text-left editable"><textarea class="item-spec">' + (item.SPECIFICATION || '') + '</textarea></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-qty" value="' + (item.QUANTITY || '') + '"></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-unit" value="' + (item.UNIT || 'EA') + '"></td>';
itemsHtml += '<td class="text-right editable"><input type="text" class="item-price" value="' + (item.UNIT_PRICE ? addComma(item.UNIT_PRICE) : '') + '"></td>';
itemsHtml += '<td class="text-right editable"><input type="text" class="item-amount" value="' + (item.AMOUNT ? addComma(item.AMOUNT) : '₩0') + '" readonly></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-note" value="' + (item.NOTE || '') + '"></td>';
itemsHtml += '</tr>';
}
$("#itemsTableBody").html(itemsHtml);
}
// 비고 로드
$("#note1").val(data.estimate.NOTE1 || "1. 견적유효기간: 일");
$("#note2").val(data.estimate.NOTE2 || "2. 납품기간: 발주 후 1주 이내");
$("#note3").val(data.estimate.NOTE3 || "3. VAT 별도");
$("#note4").val(data.estimate.NOTE4 || "4. 결제 조건 : 기존 결제조건에 따름.");
}
},
error: function() {
Swal.fire("데이터를 불러오는데 실패했습니다.");
}
});
}
// 저장
function fn_save() {
var items = [];
$("#itemsTableBody tr").each(function(idx) {
var row = $(this);
items.push({
seq: idx + 1,
description: row.find(".item-desc").val(),
specification: row.find(".item-spec").val(),
quantity: row.find(".item-qty").val(),
unit: row.find(".item-unit").val(),
unit_price: row.find(".item-price").val().replace(/,/g, ""),
amount: row.find(".item-amount").val().replace(/,/g, "").replace(/₩/g, ""),
note: row.find(".item-note").val()
});
});
var formData = {
objId: "<%=objId%>",
template_type: "1",
executor: $("#executor").val(),
recipient: $("#recipient").val(),
estimate_no: $("#estimate_no").val(),
contact_person: $("#contact_person").val(),
greeting_text: $("#greeting_text").val(),
note1: $("#note1").val(),
note2: $("#note2").val(),
note3: $("#note3").val(),
note4: $("#note4").val(),
items: JSON.stringify(items)
};
$.ajax({
url: "/contractMgmt/saveEstimate.do",
type: "POST",
data: formData,
dataType: "json",
success: function(data) {
if(data.result === "success") {
Swal.fire({
title: "저장되었습니다.",
icon: "success"
}).then(function() {
if(opener && opener.fn_search) {
opener.fn_search();
}
self.close();
});
} else {
Swal.fire("저장에 실패했습니다.");
}
},
error: function() {
Swal.fire("저장 중 오류가 발생했습니다.");
}
});
}
</script>
</head>
<body>
<div class="estimate-container">
<!-- 제목 -->
<div class="title">견 적 서</div>
<!-- 상단 정보 테이블 -->
<table class="info-table">
<colgroup>
<col width="80px" />
<col width="*" />
<col width="300px" />
</colgroup>
<tr>
<td class="label">시행일자</td>
<td class="editable">
<input type="text" id="executor" value="">
</td>
<td rowspan="4" style="text-align: center; border: none; vertical-align: middle; padding: 0;">
<div style="width: 100%; text-align: center; margin-bottom: 5px;">
<img src="/images/company_stamp.png" alt="회사 도장" style="width: 100%; height: auto;"
onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
<div class="company-stamp" style="display: none; width: 100%; height: 250px;">
<div class="company-stamp-text">㈊알피에스<br>RPS CO., LTD<br>대표이사이동준</div>
</div>
</div>
<div style="text-align: left; font-size: 9pt; line-height: 1.8; padding: 0 5px;">
담당자 : 영업부<br>
연락처 :
</div>
</td>
</tr>
<tr>
<td class="label">수신처</td>
<td class="editable">
<input type="text" id="recipient" value="">
</td>
</tr>
<tr>
<td class="label">수신인</td>
<td class="editable">
<input type="text" id="contact_person" value="구매 담당자님 귀하">
</td>
</tr>
<tr>
<td class="label">견적번호</td>
<td class="editable">
<input type="text" id="estimate_no" value="">
</td>
</tr>
</table>
<!-- 인사말 -->
<div style="margin-bottom: 10px; padding: 0px 5px; line-height: 1.6; font-size: 10pt;">
견적을 요청해 주셔서 대단히 감사합니다.<br>
하기와 같이 견적서를 제출합니다.
</div>
<!-- 부가세 별도 표시 -->
<div style="text-align: right; margin-top: -30px; margin-bottom: 20px; padding-right: 10px; font-size: 10pt;">
부가세 별도
</div>
<!-- 품목 테이블 -->
<table class="items-table">
<thead>
<tr>
<th class="col-no">번호<br>NO.</th>
<th class="col-desc">품 명<br>DESCRIPTION</th>
<th class="col-spec">규 격<br>SPECIFICATION</th>
<th class="col-qty">수량<br>Q'TY</th>
<th class="col-unit">단위<br>UNIT</th>
<th class="col-price">단 가<br>UNIT<br>PRICE</th>
<th class="col-amount">금 액<br>AMOUNT</th>
<th class="col-note">비고</th>
</tr>
</thead>
<tbody id="itemsTableBody">
<tr>
<td>1</td>
<td class="text-left editable"><input type="text" class="item-desc" value=""></td>
<td class="text-left editable"><textarea class="item-spec"></textarea></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-unit" value="EA"></td>
<td class="text-right editable"><input type="text" class="item-price" value=""></td>
<td class="text-right editable"><input type="text" class="item-amount" value="₩0" readonly></td>
<td class="editable"><input type="text" class="item-note" value=""></td>
</tr>
<tr>
<td>2</td>
<td class="text-left editable"><input type="text" class="item-desc" value=""></td>
<td class="text-left editable"><textarea class="item-spec"></textarea></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-unit" value="EA"></td>
<td class="text-right editable"><input type="text" class="item-price" value=""></td>
<td class="text-right editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-note" value=""></td>
</tr>
<tr>
<td colspan="8" style="height: 100px; vertical-align: top; padding: 10px; text-align: left;">
<div style="font-weight: bold; margin-bottom: 10px; text-align: left;">&lt;비고&gt;</div>
<textarea id="note_remarks" style="width: 100%; height: 70px; border: none; resize: none; font-family: inherit; font-size: 10pt; text-align: left;"></textarea>
</td>
</tr>
</tbody>
</table>
<!-- 참조사항 섹션 -->
<div class="notes-section">
<div class="notes-title">&lt;참조사항&gt;</div>
<div class="editable"><input type="text" id="note1" value="1. 견적유효기간: 일"></div>
<div class="editable"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내"></div>
<div class="editable"><input type="text" id="note3" value="3. VAT 별도"></div>
<div class="editable"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름."></div>
</div>
<!-- 하단 회사명 -->
<div class="footer-company">
㈜알피에스
</div>
</div>
<!-- 버튼 영역 -->
<div class="btn-area no-print">
<input type="button" value="행 추가" id="btnAddRow" class="plm_btns">
<input type="button" value="인쇄" id="btnPrint" class="plm_btns">
<input type="button" value="저장" id="btnSave" class="plm_btns">
<input type="button" value="닫기" id="btnClose" class="plm_btns">
</div>
</body>
</html>

View File

@@ -0,0 +1,718 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page import="com.pms.common.utils.*"%>
<%@ page import="java.util.*" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@include file= "/init.jsp" %>
<%
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
String userId = CommonUtils.checkNull(person.getUserId());
String objId = CommonUtils.checkNull(request.getParameter("objId"));
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><%=Constants.SYSTEM_NAME%> - 장비 견적서</title>
<style type="text/css">
@media print {
@page {
size: A4;
margin: 10mm;
}
body {
margin: 0;
padding: 0;
}
.no-print {
display: none !important;
}
}
body {
font-family: "Malgun Gothic", "맑은 고딕", Arial, sans-serif;
font-size: 10pt;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
.estimate-container {
width: 210mm;
min-height: 297mm;
background: white;
margin: 0 auto;
padding: 15mm;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
box-sizing: border-box;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 20px;
}
.logo-section {
flex: 0 0 150px;
}
.logo-img {
width: 120px;
height: auto;
}
.title-section {
flex: 1;
text-align: center;
}
.title {
font-size: 24pt;
font-weight: bold;
letter-spacing: 15px;
margin-bottom: 20px;
}
.company-info {
flex: 0 0 200px;
text-align: right;
font-size: 9pt;
line-height: 1.5;
}
.company-name {
font-weight: bold;
font-size: 10pt;
margin-bottom: 5px;
}
.basic-info {
display: flex;
gap: 10px;
margin-bottom: 20px;
font-size: 10pt;
}
.basic-info-left {
flex: 0 0 40%;
}
.basic-info-right {
flex: 1;
}
.info-row {
display: flex;
margin-bottom: 5px;
}
.info-label {
width: 80px;
font-weight: bold;
}
.info-value {
flex: 1;
}
.model-header {
background-color: #90EE90;
padding: 8px;
text-align: center;
font-weight: bold;
border: 1px solid #000;
margin-bottom: 0;
}
.items-table {
width: 100%;
border-collapse: collapse;
margin-bottom: 0;
}
.items-table th,
.items-table td {
border: 1px solid #000;
padding: 8px;
font-size: 9pt;
}
.items-table th {
background-color: #E8E8E8;
font-weight: bold;
text-align: center;
}
.items-table .col-no { width: 5%; text-align: center; }
.items-table .col-desc { width: 25%; }
.items-table .col-spec { width: 30%; }
.items-table .col-qty { width: 7%; text-align: center; }
.items-table .col-price { width: 12%; text-align: right; }
.items-table .col-amount { width: 12%; text-align: right; }
.items-table .col-remark { width: 9%; text-align: center; }
.category-row {
background-color: #f0f0f0;
font-weight: bold;
}
.subtotal-row {
background-color: #FFFF00;
font-weight: bold;
}
.subtotal-row td {
text-align: center;
}
.detail-row {
height: 60px;
}
.notes-section {
margin-top: 20px;
padding: 15px;
border: 1px solid #ddd;
font-size: 9pt;
line-height: 1.8;
}
.notes-section ul {
margin: 5px 0;
padding-left: 20px;
}
.notes-section li {
margin-bottom: 5px;
}
.footer-section {
display: flex;
justify-content: space-between;
margin-top: 20px;
font-size: 8pt;
color: #666;
}
.company-footer {
text-align: right;
font-weight: bold;
font-size: 11pt;
}
input[type="text"],
textarea {
border: none;
outline: none;
background: transparent;
width: 100%;
font-family: inherit;
font-size: inherit;
}
.editable {
background-color: #fffef0;
}
.highlight {
background-color: #FFFF00 !important;
}
.vat-badge {
background-color: #FFFF00;
padding: 3px 8px;
font-weight: bold;
display: inline-block;
}
.btn-area {
text-align: center;
margin-top: 20px;
padding: 10px;
}
.plm_btns {
padding: 10px 30px;
margin: 0 5px;
font-size: 14px;
cursor: pointer;
}
@media print {
.editable {
background-color: transparent;
}
}
</style>
<script type="text/javascript">
$(function(){
// 인쇄 버튼
$("#btnPrint").click(function(){
window.print();
});
// 저장 버튼
$("#btnSave").click(function(){
if(confirm("장비 견적서를 저장하시겠습니까?")) {
fn_save();
}
});
// 닫기 버튼
$("#btnClose").click(function(){
self.close();
});
// 금액 자동 계산
$(".item-qty, .item-price").on("change keyup", function(){
fn_calculateAmount($(this).closest("tr"));
fn_calculateSubtotal($(this).closest("tbody"));
});
// 콤마 자동 추가
$(".item-price, .item-amount, .subtotal-amount").on("blur", function(){
var val = $(this).val().replace(/,/g, "");
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
}
});
// 데이터 로드
if("<%=objId%>" !== "" && "<%=objId%>" !== "-1") {
fn_loadData();
}
});
// 금액 계산
function fn_calculateAmount(row) {
if(row.hasClass("category-row") || row.hasClass("subtotal-row")) {
return;
}
var qty = row.find(".item-qty").val() || "1";
var price = row.find(".item-price").val().replace(/,/g, "") || "0";
var amount = parseInt(qty) * parseInt(price);
if(!isNaN(amount)) {
row.find(".item-amount").val(addComma(amount));
}
}
// 소계 계산
function fn_calculateSubtotal(tbody) {
var total = 0;
tbody.find("tr:not(.category-row):not(.subtotal-row)").each(function() {
var amount = $(this).find(".item-amount").val();
if(amount) {
var numAmount = parseInt(amount.replace(/,/g, "").replace(/-/g, ""));
if(!isNaN(numAmount)) {
total += numAmount;
}
}
});
tbody.find(".subtotal-amount").val(addComma(total));
}
// 콤마 추가
function addComma(num) {
var regexp = /\B(?=(\d{3})+(?!\d))/g;
return num.toString().replace(regexp, ',');
}
// 데이터 로드
function fn_loadData() {
$.ajax({
url: "/contractMgmt/getEstimateDetail.do",
type: "POST",
data: {
objId: "<%=objId%>",
template_type: "2"
},
dataType: "json",
success: function(data) {
if(data && data.estimate) {
// 기본 정보 바인딩
$("#executor_date").val(data.estimate.EXECUTOR_DATE || "");
$("#recipient").val(data.estimate.RECIPIENT || "");
$("#model_name").val(data.estimate.MODEL_NAME || "");
$("#model_code").val(data.estimate.MODEL_CODE || "RUV-RA500S");
// 품목 데이터 로드 (카테고리별로 구분된 경우)
if(data.items && data.items.length > 0) {
// 데이터 바인딩 로직
}
}
},
error: function() {
Swal.fire("데이터를 불러오는데 실패했습니다.");
}
});
}
// 저장
function fn_save() {
var categories = [];
// 각 카테고리별 데이터 수집
$(".category-section").each(function() {
var section = $(this);
var categoryName = section.data("category");
var items = [];
section.find("tr.detail-row").each(function() {
var row = $(this);
items.push({
description: row.find(".item-desc").val(),
specification: row.find(".item-spec").val(),
quantity: row.find(".item-qty").val(),
unit_price: row.find(".item-price").val().replace(/,/g, ""),
amount: row.find(".item-amount").val().replace(/,/g, ""),
remark: row.find(".item-remark").val()
});
});
var subtotal = section.find(".subtotal-amount").val().replace(/,/g, "");
categories.push({
category: categoryName,
items: items,
subtotal: subtotal
});
});
var formData = {
objId: "<%=objId%>",
template_type: "2",
executor_date: $("#executor_date").val(),
recipient: $("#recipient").val(),
model_name: $("#model_name").val(),
model_code: $("#model_code").val(),
categories: JSON.stringify(categories)
};
$.ajax({
url: "/contractMgmt/saveEstimate.do",
type: "POST",
data: formData,
dataType: "json",
success: function(data) {
if(data.result === "success") {
Swal.fire({
title: "저장되었습니다.",
icon: "success"
}).then(function() {
if(opener && opener.fn_search) {
opener.fn_search();
}
self.close();
});
} else {
Swal.fire("저장에 실패했습니다.");
}
},
error: function() {
Swal.fire("저장 중 오류가 발생했습니다.");
}
});
}
</script>
</head>
<body>
<div class="estimate-container">
<!-- 헤더 섹션 -->
<div class="header-section">
<div class="logo-section">
<div style="border: 1px solid #ccc; padding: 20px; text-align: center; font-weight: bold; color: #0066cc;">
RPS<br>
<span style="font-size: 8pt;">A division of Cimcherry</span>
</div>
</div>
<div class="title-section">
<div class="title">견 적 서</div>
</div>
<div class="company-info">
<div class="company-name">RPS CO., LTD</div>
<div>대전광역시 유성구 국제과학로 10로 8</div>
<div>TEL: (042)602-3300, FAX: (042)672-3399</div>
</div>
</div>
<!-- 기본 정보 -->
<div class="basic-info">
<div class="basic-info-left">
<div class="info-row">
<div class="info-label">시행일자 :</div>
<div class="info-value editable">
<input type="text" id="executor_date" value="">
</div>
</div>
<div class="info-row">
<div class="info-label">수신처 :</div>
<div class="info-value editable">
<input type="text" id="recipient" value="">
</div>
</div>
<div class="info-row">
<div class="info-label">품  명 :</div>
<div class="info-value editable">
<input type="text" id="model_name" value="RUV-RA500S">
</div>
</div>
</div>
</div>
<!-- 설비 Model 헤더 -->
<div class="model-header">
설비 Model : <span class="editable" style="display: inline-block; min-width: 200px;">
<input type="text" id="model_code" value="RUV-RA500S" style="text-align: center; font-weight: bold;">
</span>
</div>
<!-- 품목 테이블 헤더 -->
<table class="items-table">
<thead>
<tr>
<th class="col-no">NO</th>
<th class="col-desc">DESCRIPTION</th>
<th class="col-spec">SPECIFICATION</th>
<th class="col-qty">Q'TY</th>
<th>UNIT PRICE</th>
<th>AMOUNT</th>
<th class="col-remark">REMARK</th>
</tr>
</thead>
</table>
<!-- 1. 초음파 CNC Machine -->
<table class="items-table category-section" data-category="cnc_machine">
<tbody>
<tr class="category-row">
<td class="col-no">1</td>
<td colspan="6">초음파 CNC Machine</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable">
<div><input type="text" value="Hole 가공" style="margin-bottom: 5px;"></div>
<div class="highlight"><input type="text" class="item-spec" value="최종 견적가"></div>
</td>
<td class="editable"><input type="text" class="item-qty" value="1"></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable">
<div class="vat-badge">VAT 별도</div>
</td>
</tr>
<tr class="detail-row">
<td></td>
<td colspan="2" class="editable"><input type="text" value="기구"></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 2. 초음파 스캔용 오돔 -->
<table class="items-table category-section" data-category="scan_odom">
<tbody>
<tr class="category-row">
<td class="col-no">2</td>
<td colspan="6">초음파 스캔용 오돔</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 3. 전장 -->
<table class="items-table category-section" data-category="electric">
<tbody>
<tr class="category-row">
<td class="col-no">3</td>
<td colspan="6">전장</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
</tbody>
</table>
<!-- 4. UTILITY -->
<table class="items-table category-section" data-category="utility">
<tbody>
<tr class="category-row">
<td class="col-no">4</td>
<td colspan="6">UTILITY</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
</tbody>
</table>
<!-- 5. 자동화 -->
<table class="items-table category-section" data-category="automation">
<tbody>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
<tr class="category-row">
<td class="col-no">5</td>
<td colspan="6">자동화</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 6. Option -->
<table class="items-table category-section" data-category="option">
<tbody>
<tr class="category-row">
<td class="col-no">6</td>
<td colspan="6">Option</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 7. Set up -->
<table class="items-table category-section" data-category="setup">
<tbody>
<tr class="category-row">
<td class="col-no">7</td>
<td colspan="6">Set up</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 8. 포장/운송 -->
<table class="items-table category-section" data-category="packing">
<tbody>
<tr class="category-row">
<td class="col-no">8</td>
<td colspan="6">포장/운송</td>
</tr>
<tr class="detail-row">
<td></td>
<td class="editable"><input type="text" class="item-desc" value=""></td>
<td class="editable"><input type="text" class="item-spec" value=""></td>
<td class="editable"><input type="text" class="item-qty" value=""></td>
<td class="editable"><input type="text" class="item-price" value=""></td>
<td class="editable"><input type="text" class="item-amount" value="" readonly></td>
<td class="editable"><input type="text" class="item-remark" value=""></td>
</tr>
<tr class="subtotal-row">
<td colspan="5">Subtotal</td>
<td class="editable"><input type="text" class="subtotal-amount" value="-" readonly></td>
<td></td>
</tr>
</tbody>
</table>
<!-- 비고 섹션 -->
<div class="notes-section">
<ul style="list-style: none; padding-left: 0;">
<li>■ 최종 견적가는 부가세 별도입니다.</li>
<li>■ 장비 납기 : 발주 후 7개월</li>
<li>■ 운송 조건 : -</li>
<li>■ 결제 조건 : 계약금 : 30% / 잔금 : 70%</li>
<li style="padding-left: 20px;">- 계약금 : 발주</li>
<li style="padding-left: 20px;">- 잔금 : 검사 완료 후</li>
<li>■ 특이사항 : 최종 장비 사양 확인 후 추가 변경 발생 시 견적 재산정 진행</li>
<li>■ Warrenty Period: 1년(소모성 parts 제외)</li>
<li>■ 주의 : RPS 등의의인 초음파 스캔용 임의 탈거 또는 세척시 보증 할 수 없음.</li>
<li>■ 패킹 견적은 양산 20대 기준의 견적조건 운송 장비 전달 시 동일한 가격 적용 모로.</li>
</ul>
</div>
<!-- 푸터 -->
<div class="footer-section">
<div>* 견적유효기간: 4/5</div>
<div class="company-footer">(㈜)알피에스</div>
</div>
<div style="text-align: right; font-size: 7pt; color: #999; margin-top: 5px;">
* RPS 견적서 - 문서번호: RPS-SE-사업부명 양식 제치부작성 적용본 등을은 결제안전버리
</div>
</div>
<!-- 버튼 영역 -->
<div class="btn-area no-print">
<input type="button" value="인쇄" id="btnPrint" class="plm_btns">
<input type="button" value="저장" id="btnSave" class="plm_btns">
<input type="button" value="닫기" id="btnClose" class="plm_btns">
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

View File

@@ -0,0 +1,86 @@
-- 견적서 템플릿 테이블 생성 SQL
-- 1. 견적서 템플릿 메인 테이블
CREATE TABLE IF NOT EXISTS ESTIMATE_TEMPLATE (
OBJID SERIAL PRIMARY KEY,
CONTRACT_OBJID NUMERIC NOT NULL, -- CONTRACT_MGMT 테이블의 OBJID 참조
TEMPLATE_TYPE VARCHAR(10) NOT NULL, -- '1': 일반 견적서, '2': 장비 견적서
-- 일반 견적서 필드 (Template 1)
EXECUTOR VARCHAR(200), -- 시행일자
RECIPIENT VARCHAR(200), -- 수신처
ESTIMATE_NO VARCHAR(100), -- 견적번호
CONTACT_PERSON VARCHAR(100), -- 수신인
GREETING_TEXT TEXT, -- 인사말
-- 장비 견적서 필드 (Template 2)
MODEL_NAME VARCHAR(200), -- 설비 Model 품명
MODEL_CODE VARCHAR(100), -- 설비 Model 코드
EXECUTOR_DATE VARCHAR(50), -- 시행일자
-- 비고 필드 (공통)
NOTE1 VARCHAR(500),
NOTE2 VARCHAR(500),
NOTE3 VARCHAR(500),
NOTE4 VARCHAR(500),
-- 카테고리 정보 (장비 견적서용, JSON 형태로 저장)
CATEGORIES_JSON TEXT,
-- 시스템 필드
WRITER VARCHAR(50),
REGDATE TIMESTAMP DEFAULT NOW(),
CHG_USER_ID VARCHAR(50),
CHGDATE TIMESTAMP DEFAULT NOW(),
CONSTRAINT fk_estimate_contract FOREIGN KEY (CONTRACT_OBJID)
REFERENCES CONTRACT_MGMT(OBJID) ON DELETE CASCADE
);
-- 인덱스 생성
CREATE INDEX idx_estimate_contract ON ESTIMATE_TEMPLATE(CONTRACT_OBJID);
CREATE INDEX idx_estimate_type ON ESTIMATE_TEMPLATE(TEMPLATE_TYPE);
-- 코멘트 추가
COMMENT ON TABLE ESTIMATE_TEMPLATE IS '견적서 템플릿 정보';
COMMENT ON COLUMN ESTIMATE_TEMPLATE.CONTRACT_OBJID IS '견적요청 OBJID (CONTRACT_MGMT 참조)';
COMMENT ON COLUMN ESTIMATE_TEMPLATE.TEMPLATE_TYPE IS '템플릿 타입 (1:일반견적서, 2:장비견적서)';
-- 2. 견적서 템플릿 품목 테이블
CREATE TABLE IF NOT EXISTS ESTIMATE_TEMPLATE_ITEM (
OBJID SERIAL PRIMARY KEY,
TEMPLATE_OBJID NUMERIC NOT NULL, -- ESTIMATE_TEMPLATE 테이블의 OBJID 참조
SEQ INTEGER NOT NULL, -- 순번
CATEGORY VARCHAR(100), -- 카테고리 (장비 견적서용)
DESCRIPTION VARCHAR(500), -- 품명
SPECIFICATION TEXT, -- 규격/사양
QUANTITY VARCHAR(50), -- 수량
UNIT VARCHAR(50), -- 단위
UNIT_PRICE NUMERIC(15,2), -- 단가
AMOUNT NUMERIC(15,2), -- 금액
NOTE VARCHAR(500), -- 비고
REMARK VARCHAR(500), -- 특이사항
CONSTRAINT fk_item_template FOREIGN KEY (TEMPLATE_OBJID)
REFERENCES ESTIMATE_TEMPLATE(OBJID) ON DELETE CASCADE
);
-- 인덱스 생성
CREATE INDEX idx_item_template ON ESTIMATE_TEMPLATE_ITEM(TEMPLATE_OBJID);
CREATE INDEX idx_item_seq ON ESTIMATE_TEMPLATE_ITEM(TEMPLATE_OBJID, SEQ);
-- 코멘트 추가
COMMENT ON TABLE ESTIMATE_TEMPLATE_ITEM IS '견적서 템플릿 품목 정보';
COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.TEMPLATE_OBJID IS '견적서 템플릿 OBJID';
COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.SEQ IS '품목 순번';
COMMENT ON COLUMN ESTIMATE_TEMPLATE_ITEM.CATEGORY IS '카테고리 (장비견적서: CNC Machine, UTILITY 등)';
-- 3. CONTRACT_MGMT 테이블에 CATEGORIES_JSON 컬럼 추가 (이미 없는 경우)
-- ALTER TABLE ESTIMATE_TEMPLATE ADD COLUMN IF NOT EXISTS CATEGORIES_JSON TEXT;
-- 샘플 데이터 조회 쿼리
-- SELECT * FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = 123;
-- SELECT * FROM ESTIMATE_TEMPLATE_ITEM WHERE TEMPLATE_OBJID = 456 ORDER BY SEQ;

View File

@@ -1763,4 +1763,122 @@ public class ContractMgmtController {
}
return "/ajax/ajaxResult";
}
/**
* 견적서 양식1 (일반 견적서) 팝업
* @param session
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/contractMgmt/estimateTemplate1.do")
public String estimateTemplate1(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
String objId = CommonUtils.checkNull(paramMap.get("objId"));
try{
Map estimate = null;
List<Map> items = new ArrayList<Map>();
if(!"".equals(objId) && !"-1".equals(objId)){
// 기존 견적서 데이터 조회
estimate = contractMgmtService.getEstimateTemplateInfo(paramMap);
items = contractMgmtService.getEstimateTemplateItems(paramMap);
}
request.setAttribute("estimate", estimate);
request.setAttribute("items", items);
} catch (Exception e) {
e.printStackTrace();
}
return "/contractMgmt/estimateTemplate1";
}
/**
* 견적서 양식2 (장비 견적서) 팝업
* @param session
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/contractMgmt/estimateTemplate2.do")
public String estimateTemplate2(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
String objId = CommonUtils.checkNull(paramMap.get("objId"));
try{
Map estimate = null;
List<Map> items = new ArrayList<Map>();
if(!"".equals(objId) && !"-1".equals(objId)){
// 기존 견적서 데이터 조회
estimate = contractMgmtService.getEstimateTemplateInfo(paramMap);
items = contractMgmtService.getEstimateTemplateItems(paramMap);
}
request.setAttribute("estimate", estimate);
request.setAttribute("items", items);
} catch (Exception e) {
e.printStackTrace();
}
return "/contractMgmt/estimateTemplate2";
}
/**
* 견적서 상세 정보 조회 (AJAX)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/getEstimateDetail.do", method=RequestMethod.POST)
public Map getEstimateDetail(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
Map estimate = contractMgmtService.getEstimateTemplateInfo(paramMap);
List<Map> items = contractMgmtService.getEstimateTemplateItems(paramMap);
resultMap.put("estimate", estimate);
resultMap.put("items", items);
resultMap.put("result", "success");
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", e.getMessage());
}
return resultMap;
}
/**
* 견적서 저장 (AJAX)
* @param request
* @param paramMap
* @return
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/saveEstimate.do", method=RequestMethod.POST)
public Map saveEstimate(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
paramMap.put("userId", person.getUserId());
contractMgmtService.saveEstimateTemplate(request, paramMap);
resultMap.put("result", "success");
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", e.getMessage());
}
return resultMap;
}
}

View File

@@ -3662,5 +3662,218 @@ ORDER BY ASM.SUPPLY_NAME
-->
FROM END_COUNT E, PROJECT_STATS P, ING_COUNT I, DELAY_COUNT D, HOLD_COUNT H, NOPLAN_COUNT N
</select>
<!-- 견적서 템플릿 관련 쿼리 -->
<!-- 견적서 기본 정보 조회 -->
<select id="getEstimateTemplateInfo" parameterType="map" resultType="map">
SELECT
OBJID,
CATEGORY_CD,
CODE_NAME(CATEGORY_CD) AS CATEGORY_NAME,
CUSTOMER_OBJID,
(SELECT SUPPLY_NAME FROM SUPPLY_MNG AS O WHERE O.OBJID = T.CUSTOMER_OBJID::NUMERIC) AS CUSTOMER_NAME,
PRODUCT,
CODE_NAME(PRODUCT) AS PRODUCT_NAME,
CUSTOMER_PROJECT_NAME,
AREA_CD,
CODE_NAME(AREA_CD) AS AREA_NAME,
PAID_TYPE,
RECEIPT_DATE,
PART_NO,
PART_NAME,
SERIAL_NO,
QUANTITY,
CUSTOMER_REQUEST,
CONTRACT_CURRENCY,
CODE_NAME(CONTRACT_CURRENCY) AS CONTRACT_CURRENCY_NAME,
EXCHANGE_RATE,
DUE_DATE,
WRITER,
(SELECT USER_NAME FROM USER_INFO AS O WHERE O.USER_ID = T.WRITER) AS WRITER_NAME,
REGDATE,
TO_CHAR(REGDATE,'YYYY-MM-DD') AS REG_DATE
FROM
CONTRACT_MGMT AS T
WHERE
OBJID = #{objId}::NUMERIC
</select>
<!-- 견적서 템플릿 데이터 조회 (ESTIMATE_TEMPLATE 테이블) -->
<select id="getEstimateTemplateData" parameterType="map" resultType="map">
SELECT
OBJID,
CONTRACT_OBJID,
TEMPLATE_TYPE,
EXECUTOR,
RECIPIENT,
ESTIMATE_NO,
CONTACT_PERSON,
GREETING_TEXT,
MODEL_NAME,
MODEL_CODE,
EXECUTOR_DATE,
NOTE1,
NOTE2,
NOTE3,
NOTE4,
WRITER,
REGDATE,
CHG_USER_ID,
CHGDATE
FROM
ESTIMATE_TEMPLATE
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
<if test="template_type != null and template_type != ''">
AND TEMPLATE_TYPE = #{template_type}
</if>
ORDER BY REGDATE DESC
LIMIT 1
</select>
<!-- 견적서 템플릿 품목 조회 -->
<select id="getEstimateTemplateItems" parameterType="map" resultType="map">
SELECT
OBJID,
TEMPLATE_OBJID,
SEQ,
CATEGORY,
DESCRIPTION,
SPECIFICATION,
QUANTITY,
UNIT,
UNIT_PRICE,
AMOUNT,
NOTE,
REMARK
FROM
ESTIMATE_TEMPLATE_ITEM
WHERE
TEMPLATE_OBJID IN (
SELECT OBJID
FROM ESTIMATE_TEMPLATE
WHERE CONTRACT_OBJID = #{objId}::NUMERIC
)
ORDER BY SEQ
</select>
<!-- 견적서 템플릿 저장 -->
<insert id="insertEstimateTemplate" parameterType="map">
INSERT INTO ESTIMATE_TEMPLATE (
CONTRACT_OBJID,
TEMPLATE_TYPE,
EXECUTOR,
RECIPIENT,
ESTIMATE_NO,
CONTACT_PERSON,
GREETING_TEXT,
MODEL_NAME,
MODEL_CODE,
EXECUTOR_DATE,
NOTE1,
NOTE2,
NOTE3,
NOTE4,
WRITER,
REGDATE,
CHG_USER_ID,
CHGDATE
) VALUES (
#{objId}::NUMERIC,
#{template_type},
#{executor},
#{recipient},
#{estimate_no},
#{contact_person},
#{greeting_text},
#{model_name},
#{model_code},
#{executor_date},
#{note1},
#{note2},
#{note3},
#{note4},
#{writer},
NOW(),
#{chg_user_id},
NOW()
)
</insert>
<!-- 견적서 템플릿 수정 -->
<update id="updateEstimateTemplate" parameterType="map">
UPDATE ESTIMATE_TEMPLATE
SET
EXECUTOR = #{executor},
RECIPIENT = #{recipient},
ESTIMATE_NO = #{estimate_no},
CONTACT_PERSON = #{contact_person},
GREETING_TEXT = #{greeting_text},
MODEL_NAME = #{model_name},
MODEL_CODE = #{model_code},
EXECUTOR_DATE = #{executor_date},
NOTE1 = #{note1},
NOTE2 = #{note2},
NOTE3 = #{note3},
NOTE4 = #{note4},
CHG_USER_ID = #{chg_user_id},
CHGDATE = NOW()
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
AND TEMPLATE_TYPE = #{template_type}
</update>
<!-- 견적서 템플릿 품목 삭제 -->
<delete id="deleteEstimateTemplateItems" parameterType="map">
DELETE FROM ESTIMATE_TEMPLATE_ITEM
WHERE
TEMPLATE_OBJID IN (
SELECT OBJID
FROM ESTIMATE_TEMPLATE
WHERE CONTRACT_OBJID = #{objId}::NUMERIC
)
</delete>
<!-- 견적서 템플릿 품목 저장 -->
<insert id="insertEstimateTemplateItems" parameterType="map">
INSERT INTO ESTIMATE_TEMPLATE_ITEM (
TEMPLATE_OBJID,
SEQ,
CATEGORY,
DESCRIPTION,
SPECIFICATION,
QUANTITY,
UNIT,
UNIT_PRICE,
AMOUNT,
NOTE,
REMARK
)
SELECT
(SELECT OBJID FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = #{objId}::NUMERIC AND TEMPLATE_TYPE = #{template_type}),
(json_array_elements(#{items_json}::json)->>'seq')::INTEGER,
json_array_elements(#{items_json}::json)->>'category',
json_array_elements(#{items_json}::json)->>'description',
json_array_elements(#{items_json}::json)->>'specification',
json_array_elements(#{items_json}::json)->>'quantity',
json_array_elements(#{items_json}::json)->>'unit',
(json_array_elements(#{items_json}::json)->>'unit_price')::NUMERIC,
(json_array_elements(#{items_json}::json)->>'amount')::NUMERIC,
json_array_elements(#{items_json}::json)->>'note',
json_array_elements(#{items_json}::json)->>'remark'
</insert>
<!-- 견적서 템플릿 카테고리 업데이트 (장비 견적서용) -->
<update id="updateEstimateTemplateCategories" parameterType="map">
UPDATE ESTIMATE_TEMPLATE
SET
CATEGORIES_JSON = #{categories_json},
CHG_USER_ID = #{chg_user_id},
CHGDATE = NOW()
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
AND TEMPLATE_TYPE = #{template_type}
</update>
</mapper>

View File

@@ -1208,4 +1208,135 @@ public class ContractMgmtService {
return resultMap;
}
/**
* 견적서 템플릿 기본 정보 조회
* @param paramMap
* @return
*/
public Map getEstimateTemplateInfo(Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String templateType = CommonUtils.checkNull(paramMap.get("template_type"));
if(!"".equals(objId) && !"-1".equals(objId)){
// 견적서 기본 정보 조회 (CONTRACT_MGMT 테이블에서)
resultMap = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateInfo", paramMap);
// 견적서 템플릿 정보 조회 (ESTIMATE_TEMPLATE 테이블에서, 있는 경우)
Map templateInfo = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateData", paramMap);
if(templateInfo != null && !templateInfo.isEmpty()){
resultMap.putAll(templateInfo);
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
/**
* 견적서 템플릿 품목 정보 조회
* @param paramMap
* @return
*/
public List<Map> getEstimateTemplateItems(Map paramMap){
List<Map> resultList = new ArrayList<Map>();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
String objId = CommonUtils.checkNull(paramMap.get("objId"));
if(!"".equals(objId) && !"-1".equals(objId)){
resultList = sqlSession.selectList("contractMgmt.getEstimateTemplateItems", paramMap);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultList;
}
/**
* 견적서 템플릿 저장
* @param request
* @param paramMap
* @return
*/
public Map saveEstimateTemplate(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 userId = person.getUserId();
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String templateType = CommonUtils.checkNull(paramMap.get("template_type"));
String itemsJson = CommonUtils.checkNull(paramMap.get("items"));
String categoriesJson = CommonUtils.checkNull(paramMap.get("categories"));
paramMap.put("writer", userId);
paramMap.put("chg_user_id", userId);
// 견적서 템플릿 정보 저장/수정
Map existingTemplate = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateData", paramMap);
if(existingTemplate != null && !existingTemplate.isEmpty()){
// 기존 데이터 업데이트
sqlSession.update("contractMgmt.updateEstimateTemplate", paramMap);
} else {
// 신규 데이터 삽입
sqlSession.insert("contractMgmt.insertEstimateTemplate", paramMap);
}
// 기존 품목 삭제
sqlSession.delete("contractMgmt.deleteEstimateTemplateItems", paramMap);
// 품목 정보 저장
if(!"".equals(itemsJson)){
// JSON 파싱 및 저장 로직 (실제 구현 시 JSON 라이브러리 사용)
// 여기서는 간단히 파라미터로 받은 데이터를 저장
paramMap.put("items_json", itemsJson);
sqlSession.insert("contractMgmt.insertEstimateTemplateItems", paramMap);
}
// 카테고리 정보 저장 (장비 견적서용)
if(!"".equals(categoriesJson)){
paramMap.put("categories_json", categoriesJson);
sqlSession.update("contractMgmt.updateEstimateTemplateCategories", paramMap);
}
sqlSession.commit();
resultMap.put("result", "success");
resultMap.put("msg", Message.SAVE_SUCCESS);
}catch(Exception e){
if(sqlSession != null) sqlSession.rollback();
resultMap.put("result", "error");
resultMap.put("msg", Message.SAVE_FAILED);
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
}