1691 lines
54 KiB
Plaintext
1691 lines
54 KiB
Plaintext
<%@ 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"));
|
|
String templateObjId = CommonUtils.checkNull(request.getParameter("templateObjId"));
|
|
String apprStatus = CommonUtils.checkNull((String)request.getAttribute("apprStatus"));
|
|
boolean isApproved = "결재완료".equals(apprStatus);
|
|
%>
|
|
<!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;
|
|
table-layout: fixed; /* 고정 레이아웃으로 컬럼 너비 강제 */
|
|
}
|
|
|
|
.items-table th,
|
|
.items-table td {
|
|
border: 1px solid #000;
|
|
padding: 4px 2px; /* 패딩 축소 */
|
|
font-size: 9pt;
|
|
overflow: hidden; /* 내용이 넘칠 경우 숨김 */
|
|
word-wrap: break-word; /* 긴 단어 줄바꿈 */
|
|
}
|
|
|
|
.items-table th {
|
|
background-color: #E8E8E8;
|
|
font-weight: bold;
|
|
text-align: center;
|
|
font-size: 8pt; /* 헤더 글자 크기 축소 */
|
|
padding: 3px 2px; /* 헤더 패딩 더 축소 */
|
|
line-height: 1.2; /* 줄 간격 축소 */
|
|
}
|
|
|
|
/* colgroup으로 너비 제어하므로 클래스 기반 너비는 제거하거나 주석 처리 */
|
|
.items-table .col-no { text-align: center; }
|
|
.items-table .col-desc { }
|
|
.items-table .col-spec { }
|
|
.items-table .col-qty { text-align: center; }
|
|
.items-table .col-price { text-align: right; }
|
|
.items-table .col-amount { text-align: right; }
|
|
.items-table .col-remark { 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;
|
|
}
|
|
|
|
.subtotal-row input {
|
|
background-color: transparent;
|
|
border: none;
|
|
text-align: center;
|
|
width: 100%;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.detail-row td {
|
|
vertical-align: top;
|
|
padding: 5px 8px;
|
|
}
|
|
|
|
.notes-section {
|
|
/* margin-top: 20px; */
|
|
/* padding: 15px; */
|
|
border: 1px solid #ddd;
|
|
font-size: 9pt;
|
|
line-height: 1.8;
|
|
background-color: white;
|
|
}
|
|
|
|
.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;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
textarea.item-spec {
|
|
resize: none;
|
|
overflow: hidden;
|
|
min-height: 40px;
|
|
line-height: 1.4;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
.highlight {
|
|
background-color: #FFFF00 !important;
|
|
}
|
|
|
|
.vat-badge {
|
|
background-color: #FFFF00;
|
|
padding: 3px 8px;
|
|
font-weight: bold;
|
|
display: inline-block;
|
|
}
|
|
|
|
.estimate-btn-area {
|
|
position: fixed;
|
|
bottom: 0;
|
|
left: 0;
|
|
right: 0;
|
|
text-align: right;
|
|
padding: 15px 30px;
|
|
background-color: #ffffff;
|
|
border-top: 3px solid #007bff;
|
|
box-shadow: 0 -2px 10px rgba(0,0,0,0.1);
|
|
z-index: 1000;
|
|
}
|
|
|
|
.estimate-btn {
|
|
display: inline-block;
|
|
padding: 5px 15px;
|
|
margin: 0 2px;
|
|
font-size: 12px;
|
|
font-weight: normal;
|
|
cursor: pointer;
|
|
border: 1px solid #60a5fa;
|
|
background-color: #60a5fa;
|
|
color: white;
|
|
border-radius: 3px;
|
|
transition: all 0.3s;
|
|
line-height: 1;
|
|
vertical-align: middle;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.estimate-btn:hover {
|
|
background-color: #1e3a8a;
|
|
border-color: #1e3a8a;
|
|
box-shadow: 0 2px 6px rgba(0,123,255,0.4);
|
|
}
|
|
|
|
.estimate-btn:active {
|
|
background-color: #1e3a8a;
|
|
border-color: #1e3a8a;
|
|
box-shadow: 0 1px 3px rgba(96,165,250,0.3);
|
|
}
|
|
|
|
@media print {
|
|
.editable {
|
|
background-color: transparent;
|
|
}
|
|
|
|
textarea {
|
|
border: none !important;
|
|
overflow: hidden;
|
|
resize: none;
|
|
background: white !important;
|
|
}
|
|
|
|
input[type="text"] {
|
|
border: none !important;
|
|
background: white !important;
|
|
}
|
|
|
|
.vat-badge {
|
|
background-color: #FFFF00 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.highlight {
|
|
background-color: #FFFF00 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.subtotal-row {
|
|
background-color: #FFFF00 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.category-row {
|
|
background-color: #f0f0f0 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.items-table th {
|
|
background-color: #E8E8E8 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
|
|
.model-header {
|
|
background-color: #90EE90 !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
}
|
|
</style>
|
|
<script type="text/javascript">
|
|
// 데이터 로딩 완료 플래그 (메일 발송 시 PDF 생성용)
|
|
window.dataLoaded = false;
|
|
window.dataLoadComplete = false;
|
|
|
|
$(function(){
|
|
var isApproved = <%=isApproved%>;
|
|
|
|
// 결재완료 상태일 때 편집 불가 처리
|
|
if(isApproved) {
|
|
// 모든 입력 필드 읽기 전용으로 변경
|
|
$("input[type='text'], textarea, select").prop("readonly", true).prop("disabled", true);
|
|
$("[contenteditable='true']").prop("contenteditable", false);
|
|
|
|
// 저장 버튼과 카테고리 추가 버튼 숨기기
|
|
$("#btnSave").hide();
|
|
$("input[value='+ 카테고리 추가']").hide();
|
|
|
|
// 삭제 버튼 숨기기
|
|
$(".btn-delete-category").hide();
|
|
}
|
|
|
|
// 환종 표시 (페이지 로드 시)
|
|
var currencyName = "${estimate.CONTRACT_CURRENCY_NAME}";
|
|
if(currencyName && currencyName !== "") {
|
|
// 괄호 안의 기호만 추출 (예: "달러 ($)" -> "$")
|
|
var match = currencyName.match(/\(([^)]+)\)/);
|
|
var displayText = match ? match[1] : currencyName;
|
|
$("#currency_display").text(displayText);
|
|
}
|
|
|
|
// 시행일자 datepicker 초기화
|
|
if(!isApproved) {
|
|
$("#executor_date").datepicker({
|
|
changeMonth: true,
|
|
changeYear: true,
|
|
dateFormat: 'yy-mm-dd'
|
|
});
|
|
}
|
|
|
|
// 수신처(고객사) select2 초기화
|
|
if(!isApproved) {
|
|
var recipientValue = $("#recipient").val();
|
|
$("#recipient").select2({
|
|
width: '200px',
|
|
placeholder: '고객사 선택'
|
|
});
|
|
|
|
// 초기 선택값이 있으면 select2에 반영 (약간의 지연 후)
|
|
if(recipientValue && recipientValue !== "") {
|
|
setTimeout(function() {
|
|
$("#recipient").val(recipientValue).trigger('change.select2');
|
|
}, 100);
|
|
}
|
|
}
|
|
|
|
// 숫자만 입력 가능하도록 제한 (unit_price, subtotal)
|
|
$(document).on("keypress", ".item-price, .subtotal-amount", function(e) {
|
|
// 숫자(0-9), 백스페이스, 삭제, 탭, 엔터, 콤마만 허용
|
|
var charCode = (e.which) ? e.which : e.keyCode;
|
|
if (charCode != 8 && charCode != 9 && charCode != 13 && charCode != 44 &&
|
|
(charCode < 48 || charCode > 57)) {
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
// 인쇄 버튼
|
|
$("#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"));
|
|
// 초음파 CNC Machine의 금액이 변경되면 최종 견적가도 업데이트
|
|
if($(this).closest(".category-section").data("category") === "cnc_machine") {
|
|
fn_calculateTotal();
|
|
}
|
|
});
|
|
|
|
// 콤마 자동 추가
|
|
$(".item-price, .item-amount, .subtotal-amount").on("blur", function(){
|
|
var val = $(this).val().replace(/,/g, "");
|
|
if(!isNaN(val) && val !== "") {
|
|
$(this).val(addComma(val));
|
|
}
|
|
});
|
|
|
|
// Subtotal 입력 시 최종 견적가 자동 계산
|
|
$(".subtotal-amount").on("change keyup", function(){
|
|
fn_calculateTotal();
|
|
});
|
|
|
|
// textarea 자동 높이 조절
|
|
$("textarea.item-spec, textarea.item-desc, textarea.item-qty, textarea.item-remark").on("input", function(){
|
|
this.style.height = "auto";
|
|
this.style.height = (this.scrollHeight) + "px";
|
|
});
|
|
|
|
// 데이터 로드 (저장된 견적서를 열 때만)
|
|
if("<%=templateObjId%>" !== "" && "<%=templateObjId%>" !== "-1") {
|
|
// 로딩 표시
|
|
Swal.fire({
|
|
title: '데이터 로딩 중...',
|
|
text: '견적서 정보를 불러오고 있습니다.',
|
|
allowOutsideClick: false,
|
|
didOpen: () => {
|
|
Swal.showLoading();
|
|
}
|
|
});
|
|
fn_loadData();
|
|
} else {
|
|
// 신규 작성 시에는 바로 데이터 로딩 완료
|
|
window.dataLoadComplete = true;
|
|
// 라이브러리 로드 체크
|
|
fn_checkLibrariesAndSetDataLoaded();
|
|
}
|
|
});
|
|
|
|
// 라이브러리 로드 확인 및 dataLoaded 설정
|
|
function fn_checkLibrariesAndSetDataLoaded() {
|
|
var checkCount = 0;
|
|
var checkInterval = setInterval(function() {
|
|
checkCount++;
|
|
|
|
if(typeof html2canvas !== 'undefined' && typeof jsPDF !== 'undefined') {
|
|
console.log('라이브러리 로드 완료, dataLoaded = true 설정');
|
|
window.dataLoaded = true;
|
|
clearInterval(checkInterval);
|
|
} else if(checkCount > 300) {
|
|
console.error('라이브러리 로드 타임아웃');
|
|
window.dataLoaded = true;
|
|
clearInterval(checkInterval);
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
// 금액 계산
|
|
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_addCategory() {
|
|
// 다음 카테고리 번호 계산 (현재 전체 카테고리 개수 + 1)
|
|
var totalCategoryCount = $(".category-section:not([data-category='cnc_machine'])").length;
|
|
var newCategoryNo = totalCategoryCount + 1;
|
|
var newCategoryName = "카테고리 " + newCategoryNo;
|
|
|
|
// 새 카테고리 HTML 생성 (6컬럼 구조)
|
|
var newCategoryHtml =
|
|
'<table class="items-table category-section" data-category="category_' + newCategoryNo + '">' +
|
|
'<colgroup>' +
|
|
'<col style="width: 5%;">' +
|
|
'<col style="width: 26%;">' +
|
|
'<col style="width: 28.5%;">' +
|
|
'<col style="width: 6.5%;">' +
|
|
'<col style="width: 24%;">' +
|
|
'<col style="width: 10%;">' +
|
|
'</colgroup>' +
|
|
'<tbody>' +
|
|
'<tr class="category-row">' +
|
|
'<td rowspan="2" class="category-no">' + newCategoryNo + '</td>' +
|
|
'<td colspan="4" contenteditable="true">' + newCategoryName + '</td>' +
|
|
'<td style="text-align: center;">' +
|
|
'<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>' +
|
|
'</td>' +
|
|
'</tr>' +
|
|
'<tr class="detail-row">' +
|
|
'<td>' +
|
|
'<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;"></textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>' +
|
|
'</td>' +
|
|
'</tr>' +
|
|
'<tr class="subtotal-row">' +
|
|
'<td colspan="4">Subtotal</td>' +
|
|
'<td><input type="text" class="subtotal-amount" value=""></td>' +
|
|
'<td></td>' +
|
|
'</tr>' +
|
|
'</tbody>' +
|
|
'</table>';
|
|
|
|
// 비고 섹션 앞에 삽입
|
|
$(".notes-section").before(newCategoryHtml);
|
|
|
|
// textarea 자동 높이 조절 이벤트 바인딩
|
|
$("textarea.item-spec, textarea.item-desc, textarea.item-qty, textarea.item-qty2, textarea.item-remark").off("input").on("input", function(){
|
|
this.style.height = "auto";
|
|
this.style.height = (this.scrollHeight) + "px";
|
|
});
|
|
|
|
// Subtotal 입력 이벤트 바인딩
|
|
$(".subtotal-amount").off("change keyup").on("change keyup", function(){
|
|
fn_calculateTotal();
|
|
});
|
|
}
|
|
|
|
// 저장된 데이터로부터 카테고리 생성 (로드 시 사용) - 6컬럼 구조
|
|
function fn_createCategoryFromData(category) {
|
|
var categoryNo = category.category_no || 1;
|
|
var categoryName = category.category_name || "카테고리 " + categoryNo;
|
|
|
|
// 새 카테고리 HTML 생성
|
|
var newCategoryHtml =
|
|
'<table class="items-table category-section" data-category="' + category.category + '">' +
|
|
'<colgroup>' +
|
|
'<col style="width: 5%;">' +
|
|
'<col style="width: 26%;">' +
|
|
'<col style="width: 28.5%;">' +
|
|
'<col style="width: 6.5%;">' +
|
|
'<col style="width: 24%;">' +
|
|
'<col style="width: 10%;">' +
|
|
'</colgroup>' +
|
|
'<tbody>' +
|
|
'<tr class="category-row">' +
|
|
'<td rowspan="2" class="category-no">' + categoryNo + '</td>' +
|
|
'<td colspan="4" contenteditable="true">' + categoryName + '</td>' +
|
|
'<td style="text-align: center;">' +
|
|
'<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>' +
|
|
'</td>' +
|
|
'</tr>' +
|
|
'<tr class="detail-row">' +
|
|
'<td>' +
|
|
'<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; white-space: pre-wrap;">' + (category.description || '') + '</textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; white-space: pre-wrap;">' + (category.specification || '') + '</textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; white-space: pre-wrap; text-align: right;">' + (category.quantity || '') + '</textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; white-space: pre-wrap;">' + (category.quantity2 || '') + '</textarea>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
'<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; white-space: pre-wrap;">' + (category.remark || '') + '</textarea>' +
|
|
'</td>' +
|
|
'</tr>' +
|
|
'<tr class="subtotal-row">' +
|
|
'<td colspan="4">Subtotal</td>' +
|
|
'<td><input type="text" class="subtotal-amount" value="' + (category.subtotal ? addComma(category.subtotal) : '') + '"></td>' +
|
|
'<td></td>' +
|
|
'</tr>' +
|
|
'</tbody>' +
|
|
'</table>';
|
|
|
|
// 비고 섹션 앞에 삽입
|
|
$(".notes-section").before(newCategoryHtml);
|
|
|
|
// textarea 자동 높이 조절 이벤트 바인딩
|
|
$("textarea.item-spec, textarea.item-desc, textarea.item-qty, textarea.item-qty2, textarea.item-remark").off("input").on("input", function(){
|
|
this.style.height = "auto";
|
|
this.style.height = (this.scrollHeight) + "px";
|
|
});
|
|
|
|
// Subtotal 입력 이벤트 바인딩
|
|
$(".subtotal-amount").off("change keyup").on("change keyup", function(){
|
|
fn_calculateTotal();
|
|
});
|
|
}
|
|
|
|
// 카테고리 삭제
|
|
function fn_deleteCategory(btn) {
|
|
if(confirm("이 카테고리를 삭제하시겠습니까?")) {
|
|
var categoryTable = $(btn).closest(".category-section");
|
|
var groupName = categoryTable.data("group");
|
|
|
|
// 카테고리 테이블 삭제
|
|
categoryTable.remove();
|
|
|
|
// 그룹에 속한 경우, 해당 그룹의 남은 카테고리 확인
|
|
if(groupName) {
|
|
var remainingInGroup = $(".category-section[data-group='" + groupName + "']").length;
|
|
|
|
// 그룹의 마지막 카테고리가 삭제되면 Subtotal도 삭제
|
|
if(remainingInGroup === 0) {
|
|
$("#" + groupName + "_subtotal").remove();
|
|
}
|
|
}
|
|
|
|
// 카테고리 번호 재정렬
|
|
fn_reorderCategories();
|
|
|
|
// 최종 견적가 재계산
|
|
fn_calculateTotal();
|
|
}
|
|
}
|
|
|
|
// 카테고리 번호 재정렬 (모든 카테고리)
|
|
function fn_reorderCategories() {
|
|
var categoryNo = 1;
|
|
$(".category-section:not([data-category='cnc_machine'])").each(function() {
|
|
$(this).find(".category-no").text(categoryNo);
|
|
categoryNo++;
|
|
});
|
|
}
|
|
|
|
// 최종 견적가 계산 (4개 Subtotal 합계)
|
|
function fn_calculateTotal() {
|
|
var total = 0;
|
|
|
|
// 초음파 CNC Machine의 AMOUNT 값 추가
|
|
var cncAmount = $(".category-section[data-category='cnc_machine'] .item-amount").val();
|
|
if(cncAmount && cncAmount !== "") {
|
|
var numVal = parseInt(cncAmount.replace(/,/g, ""));
|
|
if(!isNaN(numVal)) {
|
|
total += numVal;
|
|
}
|
|
}
|
|
|
|
// 모든 Subtotal 값을 더함
|
|
$(".subtotal-amount").each(function() {
|
|
var val = $(this).val();
|
|
if(val && val !== "" && val !== "-") {
|
|
var numVal = parseInt(val.replace(/,/g, ""));
|
|
if(!isNaN(numVal)) {
|
|
total += numVal;
|
|
}
|
|
}
|
|
});
|
|
|
|
// 초음파 CNC Machine의 최종 견적가에 설정
|
|
var finalAmount = $(".category-section[data-category='cnc_machine'] .final-amount");
|
|
if(total > 0) {
|
|
finalAmount.val(addComma(total));
|
|
} else {
|
|
finalAmount.val("");
|
|
}
|
|
}
|
|
|
|
// 데이터 로드
|
|
function fn_loadData() {
|
|
$.ajax({
|
|
url: "/contractMgmt/getEstimateDetail.do",
|
|
type: "POST",
|
|
data: {
|
|
objId: "<%=objId%>",
|
|
templateObjId: "<%=templateObjId%>",
|
|
template_type: "2"
|
|
},
|
|
dataType: "json",
|
|
timeout: 120000, // 타임아웃 120초 (2분)
|
|
success: function(data) {
|
|
Swal.close(); // 로딩 닫기
|
|
console.log("=== fn_loadData 응답 ===");
|
|
console.log("data:", data);
|
|
console.log("data.estimate:", data.estimate);
|
|
if(data.estimate) {
|
|
console.log("estimate의 모든 키:", Object.keys(data.estimate));
|
|
console.log("EXECUTOR_DATE:", data.estimate.EXECUTOR_DATE);
|
|
console.log("executor_date:", data.estimate.executor_date);
|
|
console.log("PART_NAME:", data.estimate.PART_NAME);
|
|
console.log("part_name:", data.estimate.part_name);
|
|
console.log("GROUP1_SUBTOTAL:", data.estimate.GROUP1_SUBTOTAL);
|
|
console.log("group1_subtotal:", data.estimate.group1_subtotal);
|
|
}
|
|
|
|
if(data && data.estimate) {
|
|
// 기본 정보 바인딩
|
|
$("#executor_date").val(data.estimate.EXECUTOR_DATE || "");
|
|
$("#recipient").val(data.estimate.RECIPIENT || "").trigger('change');
|
|
$("#part_name").val(data.estimate.PART_NAME || "");
|
|
$("#part_objid").val(data.estimate.PART_OBJID || "");
|
|
|
|
// 환종 표시
|
|
if(data.estimate.CONTRACT_CURRENCY_NAME) {
|
|
// 괄호 안의 기호만 추출 (예: "달러 ($)" -> "$")
|
|
var match = data.estimate.CONTRACT_CURRENCY_NAME.match(/\(([^)]+)\)/);
|
|
var displayText = match ? match[1] : data.estimate.CONTRACT_CURRENCY_NAME;
|
|
$("#currency_display").text(displayText);
|
|
}
|
|
|
|
// 비고 내용 - 기존 데이터가 있을 때만 덮어쓰기
|
|
if(data.estimate.NOTES_CONTENT && data.estimate.NOTES_CONTENT !== "") {
|
|
$("#notes_content").html(data.estimate.NOTES_CONTENT);
|
|
}
|
|
|
|
// 견적 유효기간 - 기존 데이터가 있을 때만 덮어쓰기
|
|
if(data.estimate.VALIDITY_PERIOD && data.estimate.VALIDITY_PERIOD !== "") {
|
|
$("#validity_period").val(data.estimate.VALIDITY_PERIOD);
|
|
}
|
|
|
|
// 카테고리 데이터 로드 (items가 categories로 변환됨)
|
|
var categories = data.items || [];
|
|
console.log("=== 카테고리 데이터 로드 ===");
|
|
console.log("categories:", categories);
|
|
console.log("categories.length:", categories.length);
|
|
|
|
if(categories && categories.length > 0) {
|
|
// 1. 먼저 동적으로 추가된 카테고리 모두 삭제 (기본 1-7번 제외)
|
|
$(".category-section[data-category^='category_']").remove();
|
|
|
|
// 2. 저장된 카테고리 목록 생성
|
|
var savedCategoryIds = categories.map(function(cat) { return cat.category; });
|
|
console.log("저장된 카테고리 목록:", savedCategoryIds);
|
|
|
|
// 3. 기본 카테고리(1-7) 중 저장되지 않은 것은 숨기기
|
|
$(".category-section:not([data-category='cnc_machine']):not([data-category^='category_'])").each(function() {
|
|
var categoryId = $(this).data("category");
|
|
if(savedCategoryIds.indexOf(categoryId) === -1) {
|
|
console.log("카테고리 숨김:", categoryId);
|
|
$(this).hide();
|
|
} else {
|
|
$(this).show();
|
|
}
|
|
});
|
|
|
|
// 4. 카테고리별로 데이터 바인딩 또는 생성
|
|
$.each(categories, function(catIdx, category) {
|
|
console.log("카테고리 " + catIdx + ":", category);
|
|
var categorySection = $(".category-section[data-category='" + category.category + "']");
|
|
console.log(" category.category:", category.category);
|
|
console.log(" categorySection.length:", categorySection.length);
|
|
|
|
// 기존 카테고리가 없으면 동적으로 생성 (동적 추가된 카테고리)
|
|
if(categorySection.length === 0 && category.category.startsWith("category_")) {
|
|
fn_createCategoryFromData(category);
|
|
categorySection = $(".category-section[data-category='" + category.category + "']");
|
|
}
|
|
|
|
if(categorySection.length > 0) {
|
|
// 초음파 CNC Machine (특별 영역)
|
|
if(category.category === "cnc_machine" && category.items && category.items.length > 0) {
|
|
var item = category.items[0];
|
|
categorySection.find(".item-desc").val(item.description || "");
|
|
categorySection.find(".item-spec").val(item.specification || "");
|
|
categorySection.find(".item-qty").val(item.quantity || "");
|
|
categorySection.find(".item-price").val(item.unit_price ? addComma(item.unit_price) : "");
|
|
categorySection.find(".item-amount").val(item.amount ? addComma(item.amount) : "");
|
|
categorySection.find(".item-remark").val(item.remark || "");
|
|
}
|
|
// 기구~포장/물류 (일반 영역)
|
|
else {
|
|
// 카테고리 이름 바인딩
|
|
if(category.category_name) {
|
|
categorySection.find(".category-row td[contenteditable]").text(category.category_name);
|
|
}
|
|
|
|
categorySection.find(".item-desc").val(category.description || "");
|
|
categorySection.find(".item-spec").val(category.specification || "");
|
|
categorySection.find(".item-qty").val(category.quantity || "");
|
|
categorySection.find(".item-qty2").val(category.quantity2 || "");
|
|
categorySection.find(".item-remark").val(category.remark || "");
|
|
|
|
// 개별 Subtotal 바인딩 (그룹에 속하지 않은 카테고리만)
|
|
if(!category.group && category.subtotal && category.subtotal !== "") {
|
|
categorySection.find(".subtotal-amount").val(addComma(category.subtotal));
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// textarea 높이 자동 조절
|
|
$("textarea.item-spec, textarea.item-desc, textarea.item-qty, textarea.item-remark").each(function() {
|
|
this.style.height = "auto";
|
|
this.style.height = (this.scrollHeight) + "px";
|
|
});
|
|
|
|
// 카테고리 번호 재정렬
|
|
fn_reorderCategories();
|
|
}
|
|
|
|
// 그룹 Subtotal 로드 (group1)
|
|
if(data.estimate.GROUP1_SUBTOTAL) {
|
|
$("#group1_subtotal .subtotal-amount").val(addComma(data.estimate.GROUP1_SUBTOTAL));
|
|
}
|
|
|
|
// 최종 견적가 계산 (데이터 로드 후)
|
|
fn_calculateTotal();
|
|
|
|
// 데이터 로딩 완료 플래그 설정
|
|
window.dataLoadComplete = true;
|
|
// 라이브러리 로드 확인 후 dataLoaded 설정
|
|
fn_checkLibrariesAndSetDataLoaded();
|
|
}
|
|
},
|
|
error: function(xhr, status, error) {
|
|
Swal.close(); // 로딩 닫기
|
|
window.dataLoadComplete = false;
|
|
window.dataLoaded = false; // 에러 시 false로 설정
|
|
console.log("=== fn_loadData 에러 ===");
|
|
console.log("status:", status);
|
|
console.log("error:", error);
|
|
console.log("xhr:", xhr);
|
|
|
|
if(status === "timeout") {
|
|
Swal.fire({
|
|
title: "타임아웃",
|
|
text: "견적서 데이터 로딩 시간이 초과되었습니다. 서버 로그를 확인해주세요.",
|
|
icon: "error"
|
|
});
|
|
} else {
|
|
Swal.fire({
|
|
title: "데이터 로딩 실패",
|
|
text: "데이터를 불러오는데 실패했습니다. (" + status + ")",
|
|
icon: "error"
|
|
});
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 저장
|
|
function fn_save() {
|
|
// 로딩 표시
|
|
Swal.fire({
|
|
title: '저장 중...',
|
|
text: '데이터를 저장하고 있습니다.',
|
|
allowOutsideClick: false,
|
|
didOpen: () => {
|
|
Swal.showLoading();
|
|
}
|
|
});
|
|
|
|
var categories = [];
|
|
|
|
// 초음파 CNC Machine (특별 영역)
|
|
var cncSection = $(".category-section[data-category='cnc_machine']");
|
|
if(cncSection.length > 0) {
|
|
categories.push({
|
|
category: "cnc_machine",
|
|
category_name: "초음파 CNC Machine",
|
|
category_no: 0,
|
|
content: "",
|
|
items: [{
|
|
description: cncSection.find(".item-desc").val() || "",
|
|
specification: cncSection.find(".item-spec").val() || "",
|
|
quantity: cncSection.find(".item-qty").val() || "",
|
|
unit_price: (cncSection.find(".item-price").val() || "").replace(/,/g, ""),
|
|
amount: (cncSection.find(".item-amount").val() || "").replace(/,/g, ""),
|
|
remark: cncSection.find(".item-remark").val() || ""
|
|
}],
|
|
subtotal: ""
|
|
});
|
|
}
|
|
|
|
// 기구~포장/물류 (일반 영역) - 보이는 카테고리만 수집
|
|
$(".category-section:not([data-category='cnc_machine']):visible").each(function(idx) {
|
|
var section = $(this);
|
|
var categoryName = section.data("category");
|
|
var groupName = section.data("group");
|
|
// contenteditable 영역에서 카테고리 이름 가져오기
|
|
var categoryTitle = section.find(".category-row td[contenteditable]").text().trim();
|
|
var desc = section.find(".item-desc").val() || "";
|
|
var spec = section.find(".item-spec").val() || "";
|
|
var qty = section.find(".item-qty").val() || "";
|
|
var qty2 = section.find(".item-qty2").val() || "";
|
|
var remark = section.find(".item-remark").val() || "";
|
|
|
|
// 개별 Subtotal (그룹에 속하지 않은 카테고리만)
|
|
var subtotal = "";
|
|
if(!groupName) {
|
|
var subtotalVal = section.find(".subtotal-amount").val() || "-";
|
|
subtotal = subtotalVal === "-" ? "" : subtotalVal.replace(/,/g, "");
|
|
}
|
|
|
|
var categoryData = {
|
|
category: categoryName,
|
|
category_name: categoryTitle,
|
|
category_no: idx + 1,
|
|
group: groupName || "",
|
|
description: desc,
|
|
specification: spec,
|
|
quantity: qty,
|
|
quantity2: qty2,
|
|
remark: remark,
|
|
subtotal: subtotal
|
|
};
|
|
|
|
console.log("카테고리 " + (idx + 1) + " 수집:", categoryData);
|
|
categories.push(categoryData);
|
|
});
|
|
|
|
console.log("=== 저장할 전체 카테고리 ===");
|
|
console.log("categories 개수:", categories.length);
|
|
console.log("categories:", categories);
|
|
|
|
// 그룹 Subtotal 저장 (group1)
|
|
var group1Subtotal = "";
|
|
if($("#group1_subtotal").length > 0) {
|
|
var group1SubtotalVal = $("#group1_subtotal .subtotal-amount").val() || "";
|
|
group1Subtotal = group1SubtotalVal.replace(/,/g, "");
|
|
}
|
|
|
|
// 최종 견적가 (TOTAL_AMOUNT) - 콤마 제거
|
|
var finalAmountVal = $(".category-section[data-category='cnc_machine'] .final-amount").val() || "";
|
|
var totalAmount = finalAmountVal.replace(/,/g, "");
|
|
|
|
// 환율 정보 가져오기 (JSP에서 전달된 값)
|
|
var exchangeRate = parseFloat("${estimate.EXCHANGE_RATE}" || "1");
|
|
|
|
// 원화 환산 (TOTAL_AMOUNT_KRW)
|
|
var totalAmountKrw = "";
|
|
if(totalAmount && totalAmount !== "" && !isNaN(totalAmount)) {
|
|
totalAmountKrw = Math.round(parseFloat(totalAmount) * exchangeRate).toString();
|
|
}
|
|
|
|
var formData = {
|
|
objId: "<%=objId%>",
|
|
templateObjId: "<%=templateObjId%>",
|
|
template_type: "2",
|
|
executor_date: $("#executor_date").val(),
|
|
recipient: $("#recipient").val(),
|
|
part_name: $("#part_name").val(),
|
|
part_objid: $("#part_objid").val(),
|
|
notes_content: $("#notes_content").html(),
|
|
validity_period: $("#validity_period").val(),
|
|
categories: JSON.stringify(categories),
|
|
group1_subtotal: group1Subtotal,
|
|
total_amount: totalAmount,
|
|
total_amount_krw: totalAmountKrw
|
|
};
|
|
|
|
$.ajax({
|
|
url: "/contractMgmt/saveEstimate2.do",
|
|
type: "POST",
|
|
data: formData,
|
|
dataType: "json",
|
|
timeout: 120000, // 타임아웃 120초 (2분)
|
|
success: function(data) {
|
|
Swal.close(); // 로딩 닫기
|
|
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(xhr, status, error) {
|
|
Swal.close(); // 로딩 닫기
|
|
if(status === "timeout") {
|
|
Swal.fire({
|
|
title: "타임아웃",
|
|
text: "저장 시간이 초과되었습니다. 데이터가 많은 경우 시간이 오래 걸릴 수 있습니다.",
|
|
icon: "error"
|
|
});
|
|
} else {
|
|
Swal.fire("저장 중 오류가 발생했습니다.");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// PDF 생성 및 업로드 (메일 발송용)
|
|
function fn_generateAndUploadPdf(callback) {
|
|
console.log('fn_generateAndUploadPdf 호출됨');
|
|
|
|
// 라이브러리 로드 확인
|
|
if(typeof html2canvas === 'undefined' || typeof jsPDF === 'undefined') {
|
|
console.error('필요한 라이브러리가 로드되지 않았습니다.');
|
|
if(callback && typeof callback === 'function') {
|
|
callback(null);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// 버튼 영역 임시 숨김
|
|
$('.estimate-btn-area').hide();
|
|
|
|
// textarea를 div로 변환 (줄바꿈 보존)
|
|
var textareaBackup = [];
|
|
$('textarea').each(function(index) {
|
|
var $textarea = $(this);
|
|
var content = $textarea.val();
|
|
var $div = $('<div></div>').css({
|
|
'white-space': 'pre-wrap',
|
|
'word-wrap': 'break-word',
|
|
'font-family': $textarea.css('font-family'),
|
|
'font-size': $textarea.css('font-size'),
|
|
'line-height': $textarea.css('line-height'),
|
|
'padding': $textarea.css('padding'),
|
|
'text-align': $textarea.css('text-align'),
|
|
'min-height': $textarea.css('min-height')
|
|
}).text(content);
|
|
|
|
textareaBackup.push({
|
|
element: $textarea,
|
|
parent: $textarea.parent()
|
|
});
|
|
|
|
$textarea.replaceWith($div);
|
|
});
|
|
|
|
// PDF 생성을 위해 스타일 조정
|
|
var container = $('.estimate-container');
|
|
var originalBg = container.css('background');
|
|
var originalShadow = container.css('box-shadow');
|
|
var originalPadding = container.css('padding');
|
|
|
|
// 깔끔한 PDF를 위해 배경, 그림자, 패딩 제거
|
|
container.css({
|
|
'background': 'white',
|
|
'box-shadow': 'none',
|
|
'padding': '10mm'
|
|
});
|
|
|
|
// body 배경색도 흰색으로
|
|
var originalBodyBg = $('body').css('background-color');
|
|
$('body').css('background-color', 'white');
|
|
|
|
// 견적서 컨테이너 캡처
|
|
html2canvas(document.querySelector('.estimate-container'), {
|
|
scale: 2, // 적절한 해상도 (파일 크기 최적화)
|
|
useCORS: true,
|
|
logging: false,
|
|
backgroundColor: '#ffffff'
|
|
}).then(function(canvas) {
|
|
console.log('Canvas 캡처 완료');
|
|
|
|
// textarea 복원
|
|
$('div').each(function(index) {
|
|
if(index < textareaBackup.length) {
|
|
var backup = textareaBackup[index];
|
|
$(this).replaceWith(backup.element);
|
|
}
|
|
});
|
|
|
|
// 스타일 복원
|
|
container.css({
|
|
'background': originalBg,
|
|
'box-shadow': originalShadow,
|
|
'padding': originalPadding
|
|
});
|
|
$('body').css('background-color', originalBodyBg);
|
|
|
|
// 버튼 영역 다시 표시
|
|
$('.estimate-btn-area').show();
|
|
|
|
try {
|
|
// Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음)
|
|
var imgData = canvas.toDataURL('image/jpeg', 0.85); // 85% 품질
|
|
console.log('이미지 변환 완료');
|
|
|
|
// PDF 생성
|
|
var pdf = new jsPDF('p', 'mm', 'a4');
|
|
var imgWidth = 210;
|
|
var pageHeight = 297;
|
|
var imgHeight = canvas.height * imgWidth / canvas.width;
|
|
var heightLeft = imgHeight;
|
|
var position = 0;
|
|
|
|
// JPEG 이미지 추가 (압축됨)
|
|
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
|
heightLeft -= pageHeight;
|
|
|
|
while (heightLeft >= 0) {
|
|
position = heightLeft - imgHeight;
|
|
pdf.addPage();
|
|
pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight, undefined, 'FAST');
|
|
heightLeft -= pageHeight;
|
|
}
|
|
|
|
console.log('PDF 생성 완료');
|
|
|
|
// PDF를 Base64로 변환
|
|
var pdfBase64 = pdf.output('dataurlstring').split(',')[1];
|
|
console.log('PDF Base64 생성 완료, 길이:', pdfBase64.length);
|
|
|
|
// 콜백 함수 호출 (메일 발송 등에 사용)
|
|
if(callback && typeof callback === 'function') {
|
|
callback(pdfBase64);
|
|
}
|
|
} catch(pdfError) {
|
|
console.error('PDF 생성 중 오류:', pdfError);
|
|
if(callback && typeof callback === 'function') {
|
|
callback(null);
|
|
}
|
|
}
|
|
|
|
}).catch(function(error) {
|
|
// textarea 복원
|
|
textareaBackup.forEach(function(backup) {
|
|
backup.parent.append(backup.element);
|
|
});
|
|
|
|
$('.estimate-btn-area').show();
|
|
console.error('Canvas 캡처 오류:', error);
|
|
if(callback && typeof callback === 'function') {
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="estimate-container">
|
|
<!-- 헤더 섹션 -->
|
|
<div class="header-section">
|
|
<div class="logo-section">
|
|
<img src="<%=request.getContextPath()%>/images/logo.png" alt="RPS Logo" style="max-width: 120px; height: auto;">
|
|
</div>
|
|
<div class="title-section">
|
|
<div class="title">견 적 서</div>
|
|
</div>
|
|
<div style="flex: 0 0 200px; text-align: right; font-size: 9pt; line-height: 1.5;">
|
|
<!-- 빈 공간 (기존 회사정보 위치) -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 기본 정보 (좌우 배치) -->
|
|
<div style="display: flex; justify-content: space-between; padding: 0 10px;">
|
|
<div style="text-align: left; font-size: 10pt; line-height: 2;">
|
|
<div><strong>시행일자 :</strong> <input type="text" id="executor_date" class="date_icon" value="" style="width: 200px; border: none; border-bottom: 1px solid #999; padding: 2px 5px;"></div>
|
|
<div><strong>수 신 처 :</strong>
|
|
<select id="recipient" style="width: 200px; border: none; border-bottom: 1px solid #999; background: #fffef0; padding: 2px 5px; font-size: 10pt;">
|
|
<option value="">고객사 선택</option>
|
|
${code_map.customer_cd}
|
|
</select>
|
|
</div>
|
|
<div><strong>품 명 :</strong>
|
|
<input type="text" id="part_name" value="${not empty items[0] ? items[0].PART_NAME : ''}" style="width: 200px; border: none; border-bottom: 1px solid #999; padding: 2px 5px;">
|
|
<input type="hidden" id="part_objid" value="${not empty items[0] ? items[0].PART_OBJID : ''}">
|
|
</div>
|
|
</div>
|
|
<div style="text-align: right;">
|
|
<!-- 회사정보 -->
|
|
<div style="display: inline-block; min-width: 300px;">
|
|
<div style="font-weight: bold; font-size: 11pt; margin-bottom: 5px;">RPS CO., LTD</div>
|
|
<div style="font-size: 9pt; line-height: 1.5;">대전광역시 유성구 국제과학로 10로 8</div>
|
|
<div style="font-size: 9pt; line-height: 1.5;">TEL: (042)602-3300, FAX: (042)672-3399</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>
|
|
|
|
<!-- 초음파 CNC Machine (특별 영역) -->
|
|
<table class="items-table category-section" data-category="cnc_machine">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 12%;">
|
|
<col style="width: 12%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<thead>
|
|
<tr>
|
|
<th>NO</th>
|
|
<th>DESCRIPTION</th>
|
|
<th>SPECIFICATION</th>
|
|
<th>Q'TY</th>
|
|
<th>UNIT PRICE</th>
|
|
<th>AMOUNT</th>
|
|
<th>REMARK</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr class="detail-row">
|
|
<td rowspan="2">1</td>
|
|
<td rowspan="2"><input type="text" class="item-desc" value="초음파 CNC Machine" style="width: 100%; border: none; background: transparent;"></td>
|
|
<td><input type="text" class="item-spec" value="Hole 가공" style="width: 100%; border: none; background: transparent; text-align: center;"></td>
|
|
<td><input type="text" class="item-qty" value="${not empty items[0] ? items[0].QUANTITY : '1'}" style="width: 100%; border: none; background: transparent; text-align: center;"></td>
|
|
<td><input type="text" class="item-price" value="" style="width: 100%; border: none; background: transparent; text-align: right;"></td>
|
|
<td><input type="text" class="item-amount" value="" readonly style="width: 100%; border: none; background: transparent; text-align: right;"></td>
|
|
<td><input type="text" class="item-remark" value="" style="width: 100%; border: none; background: transparent;"></td>
|
|
</tr>
|
|
<tr class="subtotal-row">
|
|
<td colspan="2" style="text-align: center; font-weight: bold; font-size: 8pt;">최종 견적가</td>
|
|
<td colspan="2">
|
|
<input type="text" class="final-amount" value="" readonly style="width: 70%; border: none; background: transparent; text-align: right; font-weight: bold;">
|
|
<span id="currency_display" style="font-weight: bold; margin-left: 5px;"></span>
|
|
</td>
|
|
<td style="text-align: center; font-size: 8pt;">VAT 별도</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
|
|
<!-- 1. 기구 -->
|
|
<table class="items-table category-section" data-category="structure" data-group="group1">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">1</td>
|
|
<td colspan="4" contenteditable="true">기구</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="10" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">X,Y,Z LINEAR
|
|
LINEAR SCALE ENCODER
|
|
주물 BODY
|
|
X,Z AXIS COLUMN
|
|
COVER
|
|
LM GUIDE
|
|
LM GUIDE
|
|
TABLE
|
|
SUS 자바라 X, Y</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="10" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">
|
|
|
|
|
|
MINERAL CASTING
|
|
MINERAL CASTING
|
|
SPCC 도장
|
|
X,Y: SHS25 P급, C1 중예압
|
|
Z: SRG25 P급, C0
|
|
600 * 500
|
|
벨로우즈 멀티 커버</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="10" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;">
|
|
|
|
3
|
|
1
|
|
1
|
|
1
|
|
4
|
|
2
|
|
1
|
|
4</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="10" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="10" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 2. 초음파 스핀들 모듈 -->
|
|
<table class="items-table category-section" data-category="spindle_module" data-group="group1">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">2</td>
|
|
<td colspan="4" contenteditable="true">초음파 스핀들 모듈</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">초음파 스핀들 모듈
|
|
제너레이터</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">AS030-080H2A2.0-U
|
|
15~50kHz, 50W</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;">1
|
|
1</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 3. 전장 -->
|
|
<table class="items-table category-section" data-category="electric" data-group="group1">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">3</td>
|
|
<td colspan="4" contenteditable="true">전장</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">C-BOX
|
|
INVERTER
|
|
NC 제어기</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">Panel & Box, 공용 전기 자재, 냉각장치
|
|
DELTA MS300
|
|
SIEMENS CONTROL PANEL 828D</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;">1
|
|
1
|
|
1</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 4. UTILITY -->
|
|
<table class="items-table category-section" data-category="utility" data-group="group1">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">4</td>
|
|
<td colspan="4" contenteditable="true">UTILITY</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">공압 PANEL & HOSE
|
|
CHILLER
|
|
절삭유 공급장치
|
|
OIL 자동급유장치
|
|
절삭유 공급 필터</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">
|
|
|
|
DSD-010S
|
|
순환장치
|
|
LUBRICATION
|
|
1um 1ea, 10um 1ea</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;">1
|
|
1
|
|
1
|
|
1
|
|
1</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 1~4번 그룹 공유 Subtotal -->
|
|
<table class="items-table" id="group1_subtotal">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="subtotal-row">
|
|
<td colspan="4">Subtotal</td>
|
|
<td class="editable"><input type="text" class="subtotal-amount" data-group="group1" value=""></td>
|
|
<td></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 5. Option -->
|
|
<table class="items-table category-section" data-category="option">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">5</td>
|
|
<td colspan="4" contenteditable="true">Option</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">OMP400
|
|
NC4
|
|
ATC</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">OMP400(Renishaw)
|
|
NV4Blue(Renishaw)
|
|
-</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;">1
|
|
1
|
|
1</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
<tr class="subtotal-row">
|
|
<td colspan="4">Subtotal</td>
|
|
<td class="editable"><input type="text" class="subtotal-amount" value=""></td>
|
|
<td></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 6. Set up -->
|
|
<table class="items-table category-section" data-category="setup">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">6</td>
|
|
<td colspan="4" contenteditable="true">Set up</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">인건비 (기구+제어)
|
|
인건비 (Training)
|
|
기타(항공/숙박/교통/식비)</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
<tr class="subtotal-row">
|
|
<td colspan="4">Subtotal</td>
|
|
<td class="editable"><input type="text" class="subtotal-amount" value=""></td>
|
|
<td></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 7. 포장/물류 -->
|
|
<table class="items-table category-section" data-category="packing">
|
|
<colgroup>
|
|
<col style="width: 5%;">
|
|
<col style="width: 26%;">
|
|
<col style="width: 28.5%;">
|
|
<col style="width: 6.5%;">
|
|
<col style="width: 24%;">
|
|
<col style="width: 10%;">
|
|
</colgroup>
|
|
<tbody>
|
|
<tr class="category-row">
|
|
<td rowspan="2" class="category-no">7</td>
|
|
<td colspan="4" contenteditable="true">포장/물류</td>
|
|
<td style="text-align: center;">
|
|
<button type="button" class="btn-delete-category" onclick="fn_deleteCategory(this)" style="padding: 2px 8px; font-size: 10px; cursor: pointer; background-color: #dc3545; color: white; border: none; border-radius: 3px;">삭제</button>
|
|
</td>
|
|
</tr>
|
|
<tr class="detail-row">
|
|
<td>
|
|
<textarea class="item-desc" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;">포장비
|
|
물류비</textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-spec" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6; text-align: right;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-qty2" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
<td>
|
|
<textarea class="item-remark" rows="5" style="width: 100%; resize: vertical; font-size: 9pt; line-height: 1.6;"></textarea>
|
|
</td>
|
|
</tr>
|
|
<tr class="subtotal-row">
|
|
<td colspan="4">Subtotal</td>
|
|
<td class="editable"><input type="text" class="subtotal-amount" value=""></td>
|
|
<td></td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<!-- 비고 섹션 -->
|
|
<div class="notes-section">
|
|
<div id="notes_content" contenteditable="true" style="width: 97%; min-height: 180px; border: 1px solid #ddd; padding: 10px; font-family: inherit; font-size: 9pt; line-height: 1.8; background-color: white; overflow-y: auto;"><strong>■ 최종 견적가는 부가세 별도입니다.</strong><br>■ 장비 납기 : 발주 시 협의<br>■ 운송 조건 : 국내 RPS 에서 진행<br>국외 FOB 기준으로 운송비 / 포장비는 선 당사 부담후 실비 정산<br>■ 결제 조건 : 계약금 : 30% / 중도금 : 60% / 잔금 : 10%<br>-. 계약금 : 발주 후 7일 이내<br>-. 중도금 : 출하 전<br>-. 잔 금 : 설치 완료 후<br>■ 장비사양 : 본견적은 당사 표준 사항<br>사양 협의시 금액 변동성이 있음.<br>■ Warrenty Period: 1년(소모성 parts 제외)<br>■ 주의 : RPS 동의없이 초음파 스핀들의 임의 탈거 또는 해체시 보증 할수 없음.<br><br><div style="display: flex; justify-content: space-between; align-items: center;"><span>* 견적유효기간: 4주</span><span>㈜ 알 피 에 스</span></div></div>
|
|
</div>
|
|
|
|
<!-- 푸터 -->
|
|
<!-- <div class="footer-section">
|
|
<div><input type="text" id="validity_period" value="* 견적유효기간: 0주 " style="width: 250px; text-align: left;"></div>
|
|
<div class="company-footer">㈜ 알 피 에 스</div>
|
|
</div> -->
|
|
<div style="text-align: right; font-size: 7pt; color: #999; margin-top: 5px;">
|
|
* RPS 대외비 - 본자료는 RPS의 사전허가 없이 제3자에게 제공할 수 없음을 알려드립니다.
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 버튼 영역 -->
|
|
<div class="estimate-btn-area no-print">
|
|
<input type="button" value="+ 카테고리 추가" onclick="fn_addCategory()" class="estimate-btn"><input type="button" value="인쇄" id="btnPrint" class="estimate-btn"><input type="button" value="저장" id="btnSave" class="estimate-btn"><input type="button" value="닫기" id="btnClose" class="estimate-btn">
|
|
</div>
|
|
|
|
<!-- html2canvas 및 jsPDF 라이브러리 -->
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
|
|
<script>
|
|
// 라이브러리 로드 완료 대기 및 dataLoaded 플래그 설정
|
|
$(document).ready(function() {
|
|
// 라이브러리 로드 체크
|
|
var checkLibraries = setInterval(function() {
|
|
if(typeof html2canvas !== 'undefined' && typeof jsPDF !== 'undefined') {
|
|
console.log('PDF 라이브러리 로드 완료');
|
|
|
|
// 데이터 로딩이 완료되었는지 확인
|
|
if(window.dataLoadComplete === true) {
|
|
window.dataLoaded = true;
|
|
console.log('dataLoaded = true 설정 완료');
|
|
}
|
|
clearInterval(checkLibraries);
|
|
}
|
|
}, 100);
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|