Files
wace_plm/WebContent/WEB-INF/view/contractMgmt/estimateTemplate1.jsp

1551 lines
52 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" %>
<%
// init.jsp에서 이미 connectUserName이 설정되어 있음
String userName = connectUserName;
String userId = connectUserId;
// 전화번호는 별도로 조회 필요
String userPhone = "";
try {
com.pms.common.SqlMapConfig sqlMapConfig = com.pms.common.SqlMapConfig.getInstance();
org.apache.ibatis.session.SqlSession sqlSession = sqlMapConfig.getSqlSession(false);
Map<String, Object> paramMap = new HashMap<String, Object>();
paramMap.put("userId", userId);
Map<String, Object> userInfo = (Map<String, Object>) sqlSession.selectOne("login.getUserInfo", paramMap);
if(userInfo != null) {
// MyBatis가 소문자로 변환하므로 소문자 우선 시도
String cellPhone = (String)userInfo.get("cell_phone");
String tel = (String)userInfo.get("tel");
// 소문자로 안되면 대문자 시도 (호환성)
if(cellPhone == null) {
cellPhone = (String)userInfo.get("CELL_PHONE");
}
if(tel == null) {
tel = (String)userInfo.get("TEL");
}
userPhone = CommonUtils.checkNull(cellPhone);
if("".equals(userPhone)) {
userPhone = CommonUtils.checkNull(tel);
}
}
sqlSession.close();
} catch(Exception e) {
System.out.println("전화번호 조회 오류: " + e.getMessage());
e.printStackTrace();
}
String objId = CommonUtils.checkNull(request.getParameter("objId"));
String templateObjId = CommonUtils.checkNull(request.getParameter("templateObjId"));
%>
<!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;
padding-bottom: 100px; /* 고정 버튼 영역을 위한 하단 여백 */
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: 3px 5px;
text-align: center;
font-size: 9pt;
line-height: 1.3;
}
.items-table th {
background-color: #f0f0f0;
font-weight: bold;
}
.items-table .col-no { width: 6%; }
.items-table .col-desc { width: 20%; }
.items-table .col-spec { width: 22%; }
.items-table .col-qty { width: 7%; }
.items-table .col-unit { width: 8%; }
.items-table .col-price { width: 11%; }
.items-table .col-amount { width: 11%; }
.items-table .col-note { width: 15%; }
.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 {
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);
}
input[type="text"],
textarea {
border: none;
outline: none;
background: transparent;
width: 100%;
font-family: inherit;
font-size: inherit;
}
textarea {
resize: vertical;
min-height: 25px;
padding: 2px;
}
.editable {
background-color: transparent;
}
@media print {
.editable {
background-color: transparent;
}
}
</style>
<script type="text/javascript">
// 전역 변수로 저장 (데이터 로드 시 설정됨)
var g_contractObjId = "<%=objId%>";
var g_templateObjId = "<%=templateObjId%>";
var g_exchangeRate = 1; // 환율 (기본값 1)
var g_currencyName = "KRW"; // 통화명 (기본값 원화)
var g_apprStatus = ""; // 결재상태
var g_userPhone = "<%=userPhone%>"; // 로그인 사용자 연락처
$(function(){
// 로그인 사용자 정보 디버깅
//console.log("=== 로그인 사용자 정보 ===");
//console.log("g_userPhone:", g_userPhone);
// 로그인 사용자 정보 자동 입력
// HTML에서 이미 초기값이 설정되어 있으므로 연락처만 설정
$("#manager_contact").val(g_userPhone || "");
$("#manager_name").val('<%=connectUserDeptName%> '+' <%=connectUserName%>');
//console.log("manager_contact 설정값:", $("#manager_contact").val());
// 시행일자 datepicker 초기화
$("#executor").datepicker({
changeMonth: true,
changeYear: true,
dateFormat: 'yy-mm-dd'
});
// 수신처(고객사) select2 초기화
$("#recipient").select2({
width: '100%',
placeholder: '고객사 선택'
});
// 수신처 변경 시 수신인 자동 입력
$("#recipient").change(function(){
// 데이터 로드 중이 아닐 때만 수신인 자동 로드
if(!window.isLoadingData) {
fn_loadCustomerContact($(this).val());
}
});
// templateObjId가 있으면 기존 데이터 로드
var templateObjId = "<%=templateObjId%>";
if(templateObjId && templateObjId !== ""){
fn_loadTemplateData(templateObjId);
}
// 인쇄 버튼
$("#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"));
fn_calculateTotal(); // 합계 재계산
});
// 금액 필드 직접 수정 시에도 합계 재계산
$(document).on("change keyup", ".item-amount", function(){
fn_calculateTotal(); // 합계 재계산
});
// 콤마 자동 추가 (동적 요소 포함)
$(document).on("blur", ".item-price, .item-amount", function(){
var val = $(this).val().replace(/,/g, "").replace(/₩/g, "");
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
}
fn_calculateTotal(); // blur 시에도 합계 재계산
});
// 단가 입력 시 실시간 콤마 처리
$(document).on("input", ".item-price", function(){
var val = $(this).val().replace(/,/g, "");
var cursorPos = this.selectionStart;
var commasBefore = ($(this).val().substring(0, cursorPos).match(/,/g) || []).length;
if(!isNaN(val) && val !== "") {
$(this).val(addComma(val));
// 커서 위치 조정
var commasAfter = ($(this).val().substring(0, cursorPos).match(/,/g) || []).length;
var newPos = cursorPos + (commasAfter - commasBefore);
this.setSelectionRange(newPos, newPos);
}
});
// 데이터 로드
if("<%=objId%>" !== "" && "<%=objId%>" !== "-1") {
fn_loadData();
} else {
// 새 견적서 작성 시 기본 행의 셀렉트박스 초기화
fn_initItemDescSelect('default_item_1');
fn_initItemDescSelect('default_item_2');
// 초기 로드 시 합계 계산
fn_calculateTotal();
// 새로 등록 시 명시적으로 작성중 상태 설정
g_apprStatus = "작성중";
fn_controlButtons();
}
});
// 결재상태에 따라 버튼 표시 제어
function fn_controlButtons() {
console.log("=== fn_controlButtons 호출 ===");
console.log("g_apprStatus:", g_apprStatus);
if(g_apprStatus === "결재완료") {
console.log("결재완료 상태 - 입력 필드 비활성화");
// 결재완료된 경우 행추가, 저장 버튼 숨김
$("#btnAddRow").hide();
$("#btnSave").hide();
// 모든 입력 필드를 읽기 전용으로 변경
$("input, textarea").attr("readonly", true);
$("input, textarea").css("background-color", "#f5f5f5");
// select 박스 비활성화 (고객사 선택 등)
$("select").attr("disabled", true);
$("select").css("background-color", "#f5f5f5");
// datepicker 비활성화
$("#executor").datepicker("option", "disabled", true);
// 삭제 버튼 숨김
$(".btn-delete-row").hide();
} else {
console.log("결재완료 아님 - 입력 필드 활성화");
// 결재완료가 아닌 경우 버튼 표시
$("#btnAddRow").show();
$("#btnSave").show();
// 입력 필드 활성화
$("input, textarea").attr("readonly", false);
$("input, textarea").css("background-color", "");
// select 박스 활성화
$("select").attr("disabled", false);
$("select").css("background-color", "");
// datepicker 활성화
$("#executor").datepicker("option", "disabled", false);
// 삭제 버튼 표시
$(".btn-delete-row").show();
}
}
// 금액 계산
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 fn_calculateTotal() {
var total = 0;
// 품목 행만 순회 (계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외)
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row").each(function(){
var amount = $(this).find(".item-amount").val() || "0";
// 콤마와 통화 기호 제거 후 숫자로 변환
amount = amount.replace(/,/g, "").replace(/₩/g, "").replace(/\$/g, "").replace(/€/g, "").replace(/¥/g, "");
var numAmount = parseInt(amount) || 0;
total += numAmount;
});
// 합계 행에 통화 기호와 함께 표시
var currencySymbol = getCurrencySymbol();
$("#totalAmount").text(currencySymbol + addComma(total));
// 원화환산 금액 계산 및 표시
fn_calculateTotalKRW(total);
}
// 원화환산 공급가액 계산
function fn_calculateTotalKRW(total) {
var totalKRW = total * g_exchangeRate;
$("#totalAmountKRW").text("₩" + addComma(Math.round(totalKRW)));
}
// 콤마 추가
function addComma(num) {
var regexp = /\B(?=(\d{3})+(?!\d))/g;
return num.toString().replace(regexp, ',');
}
// 통화 기호 반환
function getCurrencySymbol() {
if(g_currencyName.indexOf("달러") >= 0 || g_currencyName === "USD") {
return "$";
} else if(g_currencyName.indexOf("유로") >= 0 || g_currencyName === "EUR") {
return "€";
} else if(g_currencyName.indexOf("엔") >= 0 || g_currencyName === "JPY") {
return "¥";
} else if(g_currencyName.indexOf("위안") >= 0 || g_currencyName === "CNY") {
return "¥";
} else {
return "₩"; // 기본값 원화
}
}
// 품명 셀렉트박스 초기화 함수
function fn_initItemDescSelect(itemId) {
$("#" + itemId + " .item-desc-select").select2({
placeholder: "품명 입력하여 검색...",
allowClear: true,
width: '100%',
minimumInputLength: 1,
language: {
inputTooShort: function() {
return "최소 1글자 이상 입력하세요";
},
searching: function() {
return "검색 중...";
},
noResults: function() {
return "검색 결과가 없습니다";
}
},
ajax: {
url: '/contractMgmt/searchPartList.do',
dataType: 'json',
type: 'POST',
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
delay: 250,
data: function(params) {
return {
searchTerm: params.term
};
},
processResults: function(data) {
var results = $.map(data, function(item) {
var objId = item.OBJID || item.objid || item.objId;
var partNo = item.PART_NO || item.part_no || item.partNo;
var partName = item.PART_NAME || item.part_name || item.partName;
var spec = item.SPECIFICATION || item.specification || '';
return {
id: objId,
text: partName,
partName: partName,
partNo: partNo,
spec: spec
};
});
return {
results: results
};
},
cache: true
}
});
// 품명 선택 시 hidden 필드와 규격 자동 입력 (select2:select는 같은 값 재선택 시에도 발생)
$("#" + itemId + " .item-desc-select").on('select2:select', function(e) {
var selectedData = e.params.data;
if(selectedData) {
$("#" + itemId + " .item-desc").val(selectedData.text);
$("#" + itemId + " .item-part-objid").val(selectedData.id); // part_objid 저장
if(selectedData.spec) {
$("#" + itemId + " .item-spec").val(selectedData.spec);
}
}
});
// 품명 선택 해제 시 (X 버튼 클릭)
$("#" + itemId + " .item-desc-select").on('select2:clear', function() {
$("#" + itemId + " .item-desc").val('');
$("#" + itemId + " .item-part-objid").val(''); // part_objid 초기화
});
}
// 행 추가 함수
function fn_addItemRow() {
// 계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외하고 품목 행 개수 계산
var itemRows = $("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row");
var nextNo = itemRows.length + 1;
// 새 행 생성
var itemId = 'item_' + new Date().getTime();
var newRow = '<tr id="' + itemId + '">' +
'<td>' + nextNo + '</td>' +
'<td class="text-left editable">' +
'<select class="item-desc-select" style="width:100%;"></select>' +
'<input type="hidden" class="item-desc" value="">' +
'<input type="hidden" class="item-part-objid" 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>';
// 계 행 바로 위에 추가
$(".total-row").before(newRow);
// 품명 셀렉트박스 초기화
fn_initItemDescSelect(itemId);
// 합계 재계산
fn_calculateTotal();
}
// 데이터 로드
function fn_loadData() {
$.ajax({
url: "/contractMgmt/getEstimateDetail.do",
type: "POST",
data: {
objId: "<%=objId%>"
},
dataType: "json",
success: function(data) {
if(data && data.estimate) {
// 환율 정보 저장 (소문자 우선)
var exchangeRate = data.estimate.exchange_rate || data.estimate.EXCHANGE_RATE || "1";
var currencyName = data.estimate.contract_currency_name || data.estimate.CONTRACT_CURRENCY_NAME || "KRW";
g_exchangeRate = parseFloat(exchangeRate);
g_currencyName = currencyName;
// 결재상태 저장
g_apprStatus = data.estimate.APPR_STATUS || "작성중";
// 데이터 바인딩
$("#executor").val(data.estimate.EXECUTOR || "");
// 데이터 로드 중 플래그 설정 (수신인 자동 로드 방지)
window.isLoadingData = true;
// 수신처 설정
var recipientValue = data.estimate.RECIPIENT || "";
$("#recipient").val(recipientValue).trigger('change');
// 플래그 해제
setTimeout(function() {
window.isLoadingData = false;
}, 100);
$("#estimate_no").val(data.estimate.ESTIMATE_NO || "");
$("#contact_person").val(data.estimate.CONTACT_PERSON || "");
$("#greeting_text").val(data.estimate.GREETING_TEXT || "견적을 요청해 주셔서 대단히 감사합니다.\n하기와 같이 견적서를 제출합니다.");
// 담당자/연락처는 저장된 값이 있을 때만 덮어씀
var managerName = data.estimate.MANAGER_NAME || "";
var managerContact = data.estimate.MANAGER_CONTACT || "";
if(managerName && managerName !== "" && managerName !== "영업부") {
$("#manager_name").val(managerName);
}
if(managerContact && managerContact !== "") {
$("#manager_contact").val(managerContact);
}
/* 품목 데이터 로드 - 주석처리 (새로 작성 시 빈 템플릿 사용)
if(data.items && data.items.length > 0) {
// 기존 행 초기화 후 데이터 추가
var itemsHtml = "";
for(var i = 0; i < data.items.length; i++) {
var item = data.items[i];
var itemId = 'loaded_item_' + i;
itemsHtml += '<tr id="' + itemId + '">';
itemsHtml += '<td>' + (i + 1) + '</td>';
itemsHtml += '<td class="text-left editable">';
itemsHtml += '<select class="item-desc-select" style="width:100%;">';
// PART_OBJID가 없으면 기존 텍스트를 옵션으로 표시
var description = item.DESCRIPTION || '';
if(description) {
itemsHtml += '<option value="" selected>' + description + '</option>';
}
itemsHtml += '</select>';
itemsHtml += '<input type="hidden" class="item-desc" value="' + description + '">';
itemsHtml += '<input type="hidden" class="item-part-objid" value="' + (item.PART_OBJID || item.part_objid || '') + '">';
itemsHtml += '</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) : '') + '" readonly></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-note" value="' + (item.NOTE || '') + '"></td>';
itemsHtml += '</tr>';
}
// 계 행 추가
itemsHtml += '<tr class="total-row">';
itemsHtml += '<td colspan="6" style="text-align: center; font-weight: bold; background-color: #f0f0f0;">계</td>';
itemsHtml += '<td class="text-right" style="font-weight: bold; background-color: #f0f0f0;"><span id="totalAmount">0</span></td>';
itemsHtml += '<td style="background-color: #f0f0f0;"></td>';
itemsHtml += '</tr>';
// 원화환산 공급가액 행 추가 (숨김)
itemsHtml += '<tr class="total-krw-row" style="display: none;">';
itemsHtml += '<td colspan="6" style="text-align: center; font-weight: bold; background-color: #e8f4f8;">원화환산 공급가액 (KRW)</td>';
itemsHtml += '<td class="text-right" style="font-weight: bold; background-color: #e8f4f8;"><span id="totalAmountKRW">0</span></td>';
itemsHtml += '<td style="background-color: #e8f4f8;"></td>';
itemsHtml += '</tr>';
// 비고 행 추가
itemsHtml += '<tr class="remarks-row">';
itemsHtml += '<td colspan="8" style="height: 100px; vertical-align: top; padding: 10px; text-align: left;">';
itemsHtml += '<div style="font-weight: bold; margin-bottom: 10px; text-align: left;">&lt;비고&gt;</div>';
itemsHtml += '<textarea id="note_remarks" style="width: 100%; height: 70px; border: none; resize: none; font-family: inherit; font-size: 10pt; text-align: left;"></textarea>';
itemsHtml += '</td>';
itemsHtml += '</tr>';
$("#itemsTableBody").html(itemsHtml);
// 로드된 품목의 셀렉트박스 초기화 및 데이터 설정
for(var i = 0; i < data.items.length; i++) {
var item = data.items[i];
var itemId = 'loaded_item_' + i;
var partObjId = item.PART_OBJID || item.part_objid || '';
// PART_OBJID가 있으면 해당 품목을 셀렉트박스에 설정
if(partObjId) {
// AJAX로 품목 정보 조회하여 셀렉트박스에 옵션 추가
$.ajax({
url: '/contractMgmt/searchPartList.do',
type: 'POST',
data: { partObjId: partObjId },
dataType: 'json',
async: false, // 동기 처리
success: function(partData) {
if(partData && partData.length > 0) {
var part = partData[0];
var objId = part.OBJID || part.objid || part.objId;
var partName = part.PART_NAME || part.part_name || part.partName;
var spec = part.SPEC || part.spec || '';
// 옵션 생성 및 추가 (selected 상태로)
var newOption = new Option(partName, objId, true, true);
$("#" + itemId + " .item-desc-select").append(newOption);
// hidden 필드 업데이트
$("#" + itemId + " .item-desc").val(partName);
$("#" + itemId + " .item-part-objid").val(objId);
// 규격이 있으면 자동 입력
if(spec) {
$("#" + itemId + " .item-spec").val(spec);
}
}
}
});
}
// 셀렉트박스 초기화 (옵션 추가 후)
fn_initItemDescSelect(itemId);
}
// 비고 로드 (테이블 생성 직후)
$("#note_remarks").val(data.estimate.NOTE_REMARKS || "");
// 합계 계산
fn_calculateTotal();
}
*/ // 품목 데이터 로드 주석처리 끝
// 새로 작성 시 기본 행의 셀렉트박스 초기화
fn_initItemDescSelect('default_item_1');
fn_initItemDescSelect('default_item_2');
// 초기 로드 시 합계 계산
fn_calculateTotal();
// 하단 비고 로드
$("#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. 결제 조건 : 기존 결제조건에 따름.");
// 결재상태에 따라 버튼 제어
fn_controlButtons();
}
},
error: function() {
Swal.fire("데이터를 불러오는데 실패했습니다.");
}
});
}
// 기존 견적서 데이터 로드 (templateObjId 기준)
function fn_loadTemplateData(templateObjId){
$.ajax({
url: "/contractMgmt/getEstimateTemplateDataByObjId.do",
type: "POST",
data: { templateObjId: templateObjId },
dataType: "json",
success: function(data){
console.log("견적서 데이터:", data); // 디버깅용
if(data.result === "success" && data.template){
var template = data.template;
// CONTRACT_OBJID를 전역 변수에 저장 (저장 시 사용)
var contractObjId = template.CONTRACT_OBJID || template.contract_objid || template.contractObjId || "";
if(contractObjId) {
g_contractObjId = contractObjId;
}
// 환율 정보 저장 (소문자 우선)
var exchangeRate = template.exchange_rate || template.EXCHANGE_RATE || template.exchangeRate || "1";
var currencyName = template.contract_currency_name || template.CONTRACT_CURRENCY_NAME || template.contractCurrencyName || "KRW";
g_exchangeRate = parseFloat(exchangeRate);
g_currencyName = currencyName;
// 결재상태 저장
g_apprStatus = template.appr_status || template.APPR_STATUS || template.apprStatus || "작성중";
// 대문자/소문자 모두 지원
var executor = template.EXECUTOR || template.executor || "";
var recipient = template.RECIPIENT || template.recipient || "";
var recipientName = template.RECIPIENT_NAME || template.recipient_name || template.recipientName || "";
var estimateNo = template.ESTIMATE_NO || template.estimate_no || template.estimateNo || "";
var contactPerson = template.CONTACT_PERSON || template.contact_person || template.contactPerson || "";
var greetingText = template.GREETING_TEXT || template.greeting_text || template.greetingText || "";
var note1 = template.NOTE1 || template.note1 || "";
var note2 = template.NOTE2 || template.note2 || "";
var note3 = template.NOTE3 || template.note3 || "";
var note4 = template.NOTE4 || template.note4 || "";
var managerName = template.MANAGER_NAME || template.manager_name || template.managerName || "영업부";
var managerContact = template.MANAGER_CONTACT || template.manager_contact || template.managerContact || "";
// 기본 정보 채우기
$("#executor").val(executor);
// 데이터 로드 중 플래그 설정 (수신인 자동 로드 방지)
window.isLoadingData = true;
// 수신처 설정
if(recipient && recipient !== "") {
// OBJID로 셀렉트박스 선택
$("#recipient").val(recipient).trigger('change');
}
// 플래그 해제
setTimeout(function() {
window.isLoadingData = false;
}, 100);
$("#estimate_no").val(estimateNo);
$("#contact_person").val(contactPerson);
$("#greeting_text").val(greetingText);
// 담당자/연락처는 저장된 값이 있을 때만 덮어씀
if(managerName && managerName !== "" && managerName !== "영업부") {
$("#manager_name").val(managerName);
}
if(managerContact && managerContact !== "") {
$("#manager_contact").val(managerContact);
}
// 테이블 내 비고는 나중에 설정 (textarea 생성 후)
var noteRemarks = template.NOTE_REMARKS || template.note_remarks || template.noteRemarks || "";
// 품목 데이터 채우기
if(data.items && data.items.length > 0){
$("#itemsTableBody").empty();
// 품목 HTML 생성
var itemsHtml = "";
for(var i = 0; i < data.items.length; i++) {
var item = data.items[i];
var itemId = 'template_item_' + i;
var description = item.DESCRIPTION || item.description || '';
var specification = item.SPECIFICATION || item.specification || '';
var quantity = item.QUANTITY || item.quantity || '';
var unit = item.UNIT || item.unit || '';
var unitPrice = item.UNIT_PRICE || item.unit_price || item.unitPrice || '';
var amount = item.AMOUNT || item.amount || '';
var note = item.NOTE || item.note || '';
var partObjId = item.PART_OBJID || item.part_objid || '';
// 단가와 금액에 콤마 추가
var unitPriceFormatted = unitPrice ? addComma(unitPrice) : '';
var amountFormatted = amount ? addComma(amount) : '';
itemsHtml += '<tr id="' + itemId + '">';
itemsHtml += '<td>' + (i + 1) + '</td>';
itemsHtml += '<td class="text-left editable">';
itemsHtml += '<select class="item-desc-select" style="width:100%;">';
// PART_OBJID가 없으면 기존 텍스트를 옵션으로 표시
if(description) {
itemsHtml += '<option value="" selected>' + description + '</option>';
}
itemsHtml += '</select>';
itemsHtml += '<input type="hidden" class="item-desc" value="' + description + '">';
itemsHtml += '<input type="hidden" class="item-part-objid" value="' + partObjId + '">';
itemsHtml += '</td>';
itemsHtml += '<td class="text-left editable"><textarea class="item-spec">' + specification + '</textarea></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-qty" value="' + quantity + '"></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-unit" value="' + unit + '"></td>';
itemsHtml += '<td class="text-right editable"><input type="text" class="item-price" value="' + unitPriceFormatted + '"></td>';
itemsHtml += '<td class="text-right editable"><input type="text" class="item-amount" value="' + amountFormatted + '" readonly></td>';
itemsHtml += '<td class="editable"><input type="text" class="item-note" value="' + note + '"></td>';
itemsHtml += '</tr>';
}
// 계 행 추가
itemsHtml += '<tr class="total-row">';
itemsHtml += '<td colspan="6" style="text-align: center; font-weight: bold; background-color: #f0f0f0;">계</td>';
itemsHtml += '<td class="text-right" style="font-weight: bold; background-color: #f0f0f0;"><span id="totalAmount">0</span></td>';
itemsHtml += '<td style="background-color: #f0f0f0;"></td>';
itemsHtml += '</tr>';
// 원화환산 공급가액 행 추가 (숨김)
itemsHtml += '<tr class="total-krw-row" style="display: none;">';
itemsHtml += '<td colspan="6" style="text-align: center; font-weight: bold; background-color: #e8f4f8;">원화환산 공급가액 (KRW)</td>';
itemsHtml += '<td class="text-right" style="font-weight: bold; background-color: #e8f4f8;"><span id="totalAmountKRW">0</span></td>';
itemsHtml += '<td style="background-color: #e8f4f8;"></td>';
itemsHtml += '</tr>';
// 비고 행 추가
itemsHtml += '<tr class="remarks-row">';
itemsHtml += '<td colspan="8" style="height: 100px; vertical-align: top; padding: 10px; text-align: left;">';
itemsHtml += '<div style="font-weight: bold; margin-bottom: 10px; text-align: left;">&lt;비고&gt;</div>';
itemsHtml += '<textarea id="note_remarks" style="width: 100%; height: 70px; border: none; resize: none; font-family: inherit; font-size: 10pt; text-align: left;"></textarea>';
itemsHtml += '</td>';
itemsHtml += '</tr>';
// 참조사항 행 추가
itemsHtml += '<tr class="notes-row">';
itemsHtml += '<td colspan="8" style="vertical-align: top; padding: 10px; text-align: left; border: 1px solid #000;">';
itemsHtml += '<div style="font-weight: bold; margin-bottom: 10px; text-align: left;">&lt;참조사항&gt;</div>';
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note1" value="1. 견적유효기간: 일" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note3" value="3. VAT 별도" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
itemsHtml += '<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름." style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>';
itemsHtml += '</td>';
itemsHtml += '</tr>';
// 하단 회사명 행 추가
itemsHtml += '<tr class="footer-row">';
itemsHtml += '<td colspan="8" style="text-align: right; padding: 15px; font-size: 10pt; font-weight: bold; border: none;">';
itemsHtml += '㈜알피에스';
itemsHtml += '</td>';
itemsHtml += '</tr>';
// HTML 삽입
$("#itemsTableBody").html(itemsHtml);
// 셀렉트박스 초기화 및 데이터 설정
for(var i = 0; i < data.items.length; i++) {
var item = data.items[i];
var itemId = 'template_item_' + i;
var partObjId = item.PART_OBJID || item.part_objid || '';
// PART_OBJID가 있으면 해당 품목을 셀렉트박스에 설정
if(partObjId) {
// AJAX로 품목 정보 조회하여 셀렉트박스에 옵션 추가
$.ajax({
url: '/contractMgmt/searchPartList.do',
type: 'POST',
data: { partObjId: partObjId },
dataType: 'json',
async: false, // 동기 처리
success: function(partData) {
if(partData && partData.length > 0) {
var part = partData[0];
var objId = part.OBJID || part.objid || part.objId;
var partName = part.PART_NAME || part.part_name || part.partName;
var spec = part.SPEC || part.spec || '';
// 옵션 생성 및 추가 (selected 상태로)
var newOption = new Option(partName, objId, true, true);
$("#" + itemId + " .item-desc-select").append(newOption);
// hidden 필드 업데이트
$("#" + itemId + " .item-desc").val(partName);
$("#" + itemId + " .item-part-objid").val(objId);
// 규격이 있으면 자동 입력 (기존 값이 없을 때만)
if(spec && !$("#" + itemId + " .item-spec").val()) {
$("#" + itemId + " .item-spec").val(spec);
}
}
}
});
}
// 셀렉트박스 초기화 (옵션 추가 후)
fn_initItemDescSelect(itemId);
}
// 테이블 내 비고 값 설정 (textarea 생성 직후)
$("#note_remarks").val(noteRemarks);
// 참조사항 값 설정 (input 생성 직후)
$("#note1").val(note1 || "1. 견적유효기간: 일");
$("#note2").val(note2 || "2. 납품기간: 발주 후 1주 이내");
$("#note3").val(note3 || "3. VAT 별도");
$("#note4").val(note4 || "4. 결제 조건 : 기존 결제조건에 따름.");
// 합계 계산
fn_calculateTotal();
// 결재상태에 따라 버튼 제어
fn_controlButtons();
// 데이터 로딩 완료 플래그 설정
window.dataLoaded = true;
console.log("견적서 데이터 로딩 완료");
}
} else {
console.error("데이터 로드 실패:", data);
window.dataLoaded = false;
Swal.fire("데이터를 불러오는데 실패했습니다.");
}
},
error: function(xhr, status, error){
console.error("AJAX 오류:", xhr, status, error);
Swal.fire("데이터를 불러오는데 실패했습니다.");
}
});
}
// 저장
// 고객사 담당자 정보 로드
function fn_loadCustomerContact(customerObjId) {
if(!customerObjId || customerObjId === "") {
$("#contact_person").val("");
return;
}
$.ajax({
url: "/contractMgmt/getCustomerContactInfo.do",
type: "POST",
data: { customerObjId: customerObjId },
dataType: "json",
success: function(data) {
if(data && data.contactPerson) {
$("#contact_person").val(data.contactPerson + " 귀하");
} else {
$("#contact_person").val("구매 담당자님 귀하");
}
},
error: function() {
$("#contact_person").val("구매 담당자님 귀하");
}
});
}
function fn_save() {
var items = [];
// 계 행, 원화환산 행, 비고 행, 참조사항 행, 회사명 행 제외하고 품목 행만 저장
$("#itemsTableBody tr").not(".total-row, .total-krw-row, .remarks-row, .notes-row, .footer-row").each(function(idx) {
var row = $(this);
var quantity = row.find(".item-qty").val() || "";
var unitPrice = row.find(".item-price").val() || "";
var amount = row.find(".item-amount").val() || "";
items.push({
seq: idx + 1,
part_objid: row.find(".item-part-objid").val() || "", // part_objid 추가
description: row.find(".item-desc").val() || "",
specification: row.find(".item-spec").val() || "",
quantity: quantity.replace(/,/g, ""), // 콤마 제거
unit: row.find(".item-unit").val() || "",
unit_price: unitPrice.replace(/,/g, ""), // 콤마 제거
amount: amount.replace(/,/g, "").replace(/₩/g, ""), // 콤마와 ₩ 제거
note: row.find(".item-note").val() || ""
});
});
// objId는 CONTRACT_OBJID를 의미함
var contractObjId = g_contractObjId;
// 유효성 검사
if(!contractObjId || contractObjId === "" || contractObjId === "-1") {
Swal.fire("견적서를 저장할 수 없습니다. 영업정보가 없습니다.");
return;
}
// 합계 계산 (통화 기호와 콤마 제거한 순수 숫자)
var totalAmount = $("#totalAmount").text().replace(/[^0-9.]/g, "");
var totalAmountKRW = $("#totalAmountKRW").text().replace(/[^0-9.]/g, "");
// 디버깅: 품목 데이터 확인
console.log("저장할 품목 데이터:", items);
var formData = {
objId: contractObjId,
template_type: "1",
executor: $("#executor").val(),
recipient: $("#recipient").val(),
estimate_no: $("#estimate_no").val(),
contact_person: $("#contact_person").val(),
greeting_text: $("#greeting_text").val(),
total_amount: totalAmount, // 합계
total_amount_krw: totalAmountKRW, // 원화환산 공급가액
manager_name: $("#manager_name").val(), // 담당자
manager_contact: $("#manager_contact").val(), // 연락처
note_remarks: $("#note_remarks").val(), // 테이블 내 비고
note1: $("#note1").val(),
note2: $("#note2").val(),
note3: $("#note3").val(),
note4: $("#note4").val(),
items: JSON.stringify(items)
};
// templateObjId가 있을 때만 추가 (기존 견적서 수정 시에만)
if(g_templateObjId && g_templateObjId !== "" && g_templateObjId !== "-1") {
formData.templateObjId = g_templateObjId;
}
console.log("저장 데이터:", formData); // 디버깅용
console.log("신규 작성:", !formData.templateObjId); // 디버깅용
$.ajax({
url: "/contractMgmt/saveEstimate.do",
type: "POST",
data: formData,
dataType: "json",
success: function(data) {
console.log("저장 결과:", 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("저장에 실패했습니다." + (data.message ? "\n" + data.message : ""));
}
},
error: function(xhr, status, error) {
console.error("저장 오류:", xhr, status, error); // 디버깅용
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="50px" />
<col width="300px" />
</colgroup>
<tr>
<td class="label">시행일자</td>
<td class="editable">
<input type="text" id="executor" class="date_icon" value="" style="width: 150px;">
</td>
<td rowspan="4" style="border: none"></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>
</td>
</tr>
<tr>
<td class="label">수신처</td>
<td class="editable">
<select id="recipient" style="width: 100%; border: none; font-size: 9pt; padding: 2px;">
<option value="">고객사 선택</option>
${code_map.customer_cd}
</select>
</td>
</tr>
<tr>
<td class="label">수신인</td>
<td class="editable">
<input type="text" id="contact_person" value="구매 담당자님 귀하" readonly style="background-color: #f5f5f5;">
</td>
</tr>
<tr>
<td class="label">견적번호</td>
<td class="editable">
<input type="text" id="estimate_no" value="">
</td>
</tr>
</table>
<!-- 인사말 -->
<div style="display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 10px; padding: 0px 5px;">
<!-- 왼쪽: 인사말 -->
<div style="line-height: 1.6; font-size: 10pt;">
견적을 요청해 주셔서 대단히 감사합니다.<br>
하기와 같이 견적서를 제출합니다.
</div>
<!-- 오른쪽: 담당자 정보 및 부가세 별도 -->
<div style="text-align: right; font-size: 9pt; line-height: 1.8;">
담당자 : <input type="text" id="manager_name" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;"><br>
연락처 : <input type="text" id="manager_contact" value="" readonly style="width: 120px; border: none; border-bottom: 1px solid #ddd; font-size: 9pt; padding: 2px; background-color: #f5f5f5;"><br><br>
<span style="font-size: 10pt; margin-top: 5px; display: inline-block;">부가세 별도</span>
</div>
</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 id="default_item_1">
<td>1</td>
<td class="text-left editable">
<select class="item-desc-select" style="width:100%;"></select>
<input type="hidden" class="item-desc" value="">
<input type="hidden" class="item-part-objid" 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 id="default_item_2">
<td>2</td>
<td class="text-left editable">
<select class="item-desc-select" style="width:100%;"></select>
<input type="hidden" class="item-desc" value="">
<input type="hidden" class="item-part-objid" 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 class="total-row">
<td colspan="6" style="text-align: center; font-weight: bold; background-color: #f0f0f0;">계</td>
<td class="text-right" style="font-weight: bold; background-color: #f0f0f0;"><span id="totalAmount">0</span></td>
<td style="background-color: #f0f0f0;"></td>
</tr>
<!-- 원화환산 공급가액 행 (숨김) -->
<tr class="total-krw-row" style="display: none;">
<td colspan="6" style="text-align: center; font-weight: bold; background-color: #e8f4f8;">원화환산 공급가액 (KRW)</td>
<td class="text-right" style="font-weight: bold; background-color: #e8f4f8;"><span id="totalAmountKRW">0</span></td>
<td style="background-color: #e8f4f8;"></td>
</tr>
<!-- 비고 행 -->
<tr class="remarks-row">
<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>
<!-- 참조사항 행 -->
<tr class="notes-row">
<td colspan="8" style="vertical-align: top; padding: 10px; text-align: left; border: 1px solid #000;">
<div style="font-weight: bold; margin-bottom: 10px; text-align: left;">&lt;참조사항&gt;</div>
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note1" value="1. 견적유효기간: 일" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note2" value="2. 납품기간: 발주 후 1주 이내" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note3" value="3. VAT 별도" style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
<div class="editable" style="margin-bottom: 5px;"><input type="text" id="note4" value="4. 결제 조건 : 기존 결제조건에 따름." style="width: 100%; border: none; background: transparent; font-size: 10pt;"></div>
</td>
</tr>
<!-- 하단 회사명 행 -->
<tr class="footer-row">
<td colspan="8" style="text-align: right; padding: 15px; font-size: 10pt; font-weight: bold; border: none;">
㈜알피에스
</td>
</tr>
</tbody>
</table>
</div>
<!-- 버튼 영역 -->
<div class="btn-area no-print">
<button type="button" id="btnAddRow" class="estimate-btn">행 추가</button>
<button type="button" id="btnPrint" class="estimate-btn">인쇄</button>
<button type="button" id="btnDownloadPdf" class="estimate-btn">PDF 다운로드</button>
<button type="button" id="btnSave" class="estimate-btn">저장</button>
<button type="button" id="btnClose" class="estimate-btn">닫기</button>
</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>
// PDF 다운로드 버튼 클릭 이벤트
$("#btnDownloadPdf").click(function(){
fn_generatePdf();
});
// PDF 생성 함수
function fn_generatePdf() {
// 라이브러리 로드 확인
if(typeof html2canvas === 'undefined') {
Swal.fire({
title: '오류',
text: 'html2canvas 라이브러리가 로드되지 않았습니다.',
icon: 'error'
});
return;
}
if(typeof jsPDF === 'undefined') {
Swal.fire({
title: '오류',
text: 'jsPDF 라이브러리가 로드되지 않았습니다.',
icon: 'error'
});
return;
}
Swal.fire({
title: 'PDF 생성 중...',
text: '잠시만 기다려주세요.',
allowOutsideClick: false,
onOpen: () => {
Swal.showLoading();
}
});
// 버튼 영역 임시 숨김
$('.btn-area').hide();
// 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) {
// 스타일 복원
container.css({
'background': originalBg,
'box-shadow': originalShadow,
'padding': originalPadding
});
$('body').css('background-color', originalBodyBg);
// 버튼 영역 다시 표시
$('.btn-area').show();
try {
// Canvas를 JPEG 이미지로 변환 (PNG보다 파일 크기 작음)
var imgData = canvas.toDataURL('image/jpeg', 0.85); // 85% 품질
// PDF 생성 (A4 크기)
var pdf = new jsPDF('p', 'mm', 'a4');
var imgWidth = 210; // A4 width in mm
var pageHeight = 297; // A4 height in mm
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;
}
// 파일명 생성
var estimateNo = $("#estimate_no").val() || "견적서";
var fileName = estimateNo + '.pdf';
// PDF 다운로드
pdf.save(fileName);
Swal.close();
Swal.fire({
title: 'PDF 생성 완료',
text: 'PDF 파일이 다운로드되었습니다.',
icon: 'success',
timer: 2000
});
} catch(pdfError) {
Swal.close();
console.error('PDF 생성 오류:', pdfError);
Swal.fire({
title: '오류',
text: 'PDF 생성 중 오류가 발생했습니다: ' + pdfError.message,
icon: 'error'
});
}
}).catch(function(error) {
$('.btn-area').show();
Swal.close();
console.error('Canvas 캡처 오류:', error);
Swal.fire({
title: '오류',
text: '페이지 캡처 중 오류가 발생했습니다: ' + error.message,
icon: 'error'
});
});
}
// PDF를 Base64로 생성하여 서버로 전송하는 함수
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;
}
// 버튼 영역 임시 숨김
$('.btn-area').hide();
// 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 캡처 완료');
// 스타일 복원
container.css({
'background': originalBg,
'box-shadow': originalShadow,
'padding': originalPadding
});
$('body').css('background-color', originalBodyBg);
// 버튼 영역 다시 표시
$('.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) {
$('.btn-area').show();
console.error('Canvas 캡처 오류:', error);
if(callback && typeof callback === 'function') {
callback(null);
}
});
}
</script>
</body>
</html>