견적서 html에 비고/참조사항 추가, 일반, 장비견적서 분기 추가

This commit is contained in:
2026-02-25 14:06:06 +09:00
parent 887fe05909
commit f06a2687ea
2 changed files with 147 additions and 79 deletions

View File

@@ -717,6 +717,7 @@
ET.NOTE3,
ET.NOTE4,
ET.NOTE_REMARKS,
ET.NOTES_CONTENT,
ET.VALIDITY_PERIOD,
TO_CHAR(ET.REGDATE, 'YYYY-MM-DD') AS REGDATE,
CM.CONTRACT_NO,

View File

@@ -2146,6 +2146,7 @@ public class ApprovalService {
* 견적서 데이터를 HTML 파일로 구성 (결재 첨부파일용)
*/
private String buildEstimateFormFileHtml(Map estimateInfo, List<Map> itemList){
String templateType = CommonUtils.checkNull(estimateInfo.get("TEMPLATE_TYPE"));
String estimateNo = CommonUtils.checkNull(estimateInfo.get("ESTIMATE_NO"));
String contractNo = CommonUtils.checkNull(estimateInfo.get("CONTRACT_NO"));
String customerName = CommonUtils.checkNull(estimateInfo.get("CUSTOMER_NAME"));
@@ -2160,51 +2161,53 @@ public class ApprovalService {
String modelName = CommonUtils.checkNull(estimateInfo.get("MODEL_NAME"));
String validityPeriod = CommonUtils.checkNull(estimateInfo.get("VALIDITY_PERIOD"));
String noteRemarks = CommonUtils.checkNull(estimateInfo.get("NOTE_REMARKS"));
String note1 = CommonUtils.checkNull(estimateInfo.get("NOTE1"));
String note2 = CommonUtils.checkNull(estimateInfo.get("NOTE2"));
String note3 = CommonUtils.checkNull(estimateInfo.get("NOTE3"));
String note4 = CommonUtils.checkNull(estimateInfo.get("NOTE4"));
String notesContent = CommonUtils.checkNull(estimateInfo.get("NOTES_CONTENT"));
String managerName = CommonUtils.checkNull(estimateInfo.get("MANAGER_NAME"));
String managerContact = CommonUtils.checkNull(estimateInfo.get("MANAGER_CONTACT"));
StringBuilder html = new StringBuilder();
html.append("<!DOCTYPE html><html><head><meta charset='UTF-8'>");
html.append("<title>견적서 - " + estimateNo + "</title>");
html.append("<title>견적서 - ").append(estimateNo).append("</title>");
html.append("<style>body{font-family:'맑은 고딕',sans-serif;font-size:10pt;margin:20px;}");
html.append("table{border-collapse:collapse;width:100%;}");
html.append("th,td{border:1px solid #333;padding:6px 8px;font-size:9pt;}");
html.append("th{background-color:#4472C4;color:#fff;text-align:center;}");
html.append(".header{font-size:14pt;font-weight:bold;text-align:center;margin-bottom:15px;}");
html.append(".info-table td{border:1px solid #999;} .info-table th{background-color:#D9E2F3;color:#333;}");
html.append(".notes{margin-top:10px;padding:8px;border:1px solid #999;font-size:9pt;}");
html.append("</style></head><body>");
html.append("<div class='header'>견 적 서</div>");
// 기본 정보 테이블
html.append("<table class='info-table' style='margin-bottom:15px;'>");
html.append("<tr><th style='width:15%'>견적번호</th><td style='width:35%'>" + estimateNo + "</td>");
html.append("<th style='width:15%'>영업번호</th><td style='width:35%'>" + contractNo + "</td></tr>");
html.append("<tr><th>고객사</th><td>" + customerName + "</td>");
html.append("<th>작성일</th><td>" + regdate + "</td></tr>");
html.append("<tr><th>수신</th><td>" + recipient + "</td>");
html.append("<th>담당자</th><td>" + contactPerson + "</td></tr>");
html.append("<tr><th>작성자</th><td>" + writerName + "</td>");
html.append("<th>모델명</th><td>" + modelName + "</td></tr>");
html.append("<tr><th style='width:15%'>견적번호</th><td style='width:35%'>").append(estimateNo).append("</td>");
html.append("<th style='width:15%'>영업번호</th><td style='width:35%'>").append(contractNo).append("</td></tr>");
html.append("<tr><th>고객사</th><td>").append(customerName).append("</td>");
html.append("<th>작성일</th><td>").append(regdate).append("</td></tr>");
html.append("<tr><th>수신</th><td>").append(recipient).append("</td>");
html.append("<th>담당자</th><td>").append(contactPerson).append("</td></tr>");
html.append("<tr><th>작성자</th><td>").append(writerName).append("</td>");
html.append("<th>모델명</th><td>").append(modelName).append("</td></tr>");
if(!currencyName.isEmpty()){
html.append("<tr><th>통화</th><td>" + currencyName + "</td>");
html.append("<th>환율</th><td>" + exchangeRate + "</td></tr>");
html.append("<tr><th>통화</th><td>").append(currencyName).append("</td>");
html.append("<th>환율</th><td>").append(exchangeRate).append("</td></tr>");
}
html.append("<tr><th>합계금액</th><td colspan='3'>");
if(!totalAmountKrw.isEmpty() && !"0".equals(totalAmountKrw)){
html.append(totalAmountKrw + " (KRW)");
if(!totalAmount.isEmpty()) html.append(" / " + totalAmount + " (" + currencyName + ")");
html.append(totalAmountKrw).append(" (KRW)");
if(!totalAmount.isEmpty()) html.append(" / ").append(totalAmount).append(" (").append(currencyName).append(")");
} else {
html.append(totalAmount);
}
html.append("</td></tr>");
if(!validityPeriod.isEmpty()){
html.append("<tr><th>유효기간</th><td colspan='3'>" + validityPeriod + "</td></tr>");
}
html.append("</table>");
// 품목 리스트
if(itemList != null && !itemList.isEmpty()){
html.append("<table>");
html.append("<thead><tr>");
@@ -2215,22 +2218,44 @@ public class ApprovalService {
for(Map item : itemList){
item = CommonUtils.toUpperCaseMapKey(item);
html.append("<tr>");
html.append("<td style='text-align:center;'>" + no++ + "</td>");
html.append("<td>" + CommonUtils.checkNull(item.get("DESCRIPTION")) + "</td>");
html.append("<td>" + CommonUtils.checkNull(item.get("SPECIFICATION")) + "</td>");
html.append("<td style='text-align:right;'>" + CommonUtils.checkNull(item.get("QUANTITY")) + "</td>");
html.append("<td style='text-align:center;'>" + CommonUtils.checkNull(item.get("UNIT")) + "</td>");
html.append("<td style='text-align:right;'>" + CommonUtils.checkNull(item.get("UNIT_PRICE")) + "</td>");
html.append("<td style='text-align:right;'>" + CommonUtils.checkNull(item.get("AMOUNT")) + "</td>");
html.append("<td>" + CommonUtils.checkNull(item.get("REMARK")) + "</td>");
html.append("<td style='text-align:center;'>").append(no++).append("</td>");
html.append("<td>").append(CommonUtils.checkNull(item.get("DESCRIPTION"))).append("</td>");
html.append("<td>").append(CommonUtils.checkNull(item.get("SPECIFICATION"))).append("</td>");
html.append("<td style='text-align:right;'>").append(CommonUtils.checkNull(item.get("QUANTITY"))).append("</td>");
html.append("<td style='text-align:center;'>").append(CommonUtils.checkNull(item.get("UNIT"))).append("</td>");
html.append("<td style='text-align:right;'>").append(CommonUtils.checkNull(item.get("UNIT_PRICE"))).append("</td>");
html.append("<td style='text-align:right;'>").append(CommonUtils.checkNull(item.get("AMOUNT"))).append("</td>");
html.append("<td>").append(CommonUtils.checkNull(item.get("REMARK"))).append("</td>");
html.append("</tr>");
}
html.append("</tbody></table>");
}
// 비고
if(!noteRemarks.isEmpty()){
html.append("<br/><div><strong>비고:</strong><br/>" + noteRemarks.replace("\n", "<br/>") + "</div>");
html.append("<div class='notes'><strong>&lt;비고&gt;</strong><br/>").append(noteRemarks.replace("\n", "<br/>")).append("</div>");
}
if("2".equals(templateType)){
if(!notesContent.isEmpty()){
html.append("<div class='notes' style='line-height:1.8;'>").append(notesContent).append("</div>");
}
} else {
boolean hasNotes = !note1.isEmpty() || !note2.isEmpty() || !note3.isEmpty() || !note4.isEmpty();
if(hasNotes){
html.append("<div class='notes'><strong>&lt;참조사항&gt;</strong><br/>");
if(!note1.isEmpty()) html.append(note1).append("<br/>");
if(!note2.isEmpty()) html.append(note2).append("<br/>");
if(!note3.isEmpty()) html.append(note3).append("<br/>");
if(!note4.isEmpty()) html.append(note4);
html.append("</div>");
}
}
if(!managerName.isEmpty() || !managerContact.isEmpty()){
html.append("<div style='margin-top:10px;text-align:right;font-size:9pt;'>");
if(!managerName.isEmpty()) html.append("담당자 : ").append(managerName);
if(!managerContact.isEmpty()) html.append("<br/>연락처 : ").append(managerContact);
html.append("</div>");
}
html.append("</body></html>");
@@ -2243,6 +2268,7 @@ public class ApprovalService {
private String buildEstimateContentsHtml(Map estimateInfo, List<Map> itemList){
StringBuilder html = new StringBuilder();
String templateType = CommonUtils.checkNull(estimateInfo.get("TEMPLATE_TYPE"));
String estimateNo = CommonUtils.checkNull(estimateInfo.get("ESTIMATE_NO"));
String contractNo = CommonUtils.checkNull(estimateInfo.get("CONTRACT_NO"));
String customerName = CommonUtils.checkNull(estimateInfo.get("CUSTOMER_NAME"));
@@ -2257,87 +2283,128 @@ public class ApprovalService {
String modelName = CommonUtils.checkNull(estimateInfo.get("MODEL_NAME"));
String validityPeriod = CommonUtils.checkNull(estimateInfo.get("VALIDITY_PERIOD"));
String noteRemarks = CommonUtils.checkNull(estimateInfo.get("NOTE_REMARKS"));
String note1 = CommonUtils.checkNull(estimateInfo.get("NOTE1"));
String note2 = CommonUtils.checkNull(estimateInfo.get("NOTE2"));
String note3 = CommonUtils.checkNull(estimateInfo.get("NOTE3"));
String note4 = CommonUtils.checkNull(estimateInfo.get("NOTE4"));
String notesContent = CommonUtils.checkNull(estimateInfo.get("NOTES_CONTENT"));
String managerName = CommonUtils.checkNull(estimateInfo.get("MANAGER_NAME"));
String managerContact = CommonUtils.checkNull(estimateInfo.get("MANAGER_CONTACT"));
html.append("<div style='font-family:맑은 고딕,sans-serif;font-size:10pt;'>");
String TD_HEADER = "style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;padding:4px 8px;'";
String TD_VALUE = "style='border:1px solid #333;font-size:9pt;padding:4px 8px;'";
String TH_ITEM = "style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;padding:4px;'";
String TD_ITEM = "style='border:1px solid #333;font-size:9pt;padding:4px;'";
html.append("<div style='font-family:맑은 고딕,Malgun Gothic,sans-serif;font-size:10pt;'>");
html.append("<h3 style='text-align:center;margin-bottom:10px;'>견 적 서</h3>");
html.append("<table cellspacing='0' cellpadding='4' style='border-collapse:collapse;width:100%;margin-bottom:10px;'>");
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;width:15%;font-weight:bold;font-size:9pt;'>견적번호</td>");
html.append("<td style='border:1px solid #333;width:35%;font-size:9pt;'>" + estimateNo + "</td>");
html.append("<td style='border:1px solid #333;background-color:#D9E2F3;width:15%;font-weight:bold;font-size:9pt;'>영업번호</td>");
html.append("<td style='border:1px solid #333;width:35%;font-size:9pt;'>" + contractNo + "</td></tr>");
// 기본 정보 테이블
html.append("<table cellspacing='0' cellpadding='0' style='border-collapse:collapse;width:100%;margin-bottom:10px;'>");
html.append("<tr><td ").append(TD_HEADER).append(" width='15%'>견적번호</td>");
html.append("<td ").append(TD_VALUE).append(" width='35%'>").append(escapeHtml(estimateNo)).append("</td>");
html.append("<td ").append(TD_HEADER).append(" width='15%'>영업번호</td>");
html.append("<td ").append(TD_VALUE).append(" width='35%'>").append(escapeHtml(contractNo)).append("</td></tr>");
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>고객사</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + customerName + "</td>");
html.append("<td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>작성일</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + regdate + "</td></tr>");
html.append("<tr><td ").append(TD_HEADER).append(">고객사</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(customerName)).append("</td>");
html.append("<td ").append(TD_HEADER).append(">작성일</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(regdate)).append("</td></tr>");
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>수신</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + recipient + "</td>");
html.append("<td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>담당자</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + contactPerson + "</td></tr>");
html.append("<tr><td ").append(TD_HEADER).append(">수신</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(recipient)).append("</td>");
html.append("<td ").append(TD_HEADER).append(">담당자</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(contactPerson)).append("</td></tr>");
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>작성자</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + writerName + "</td>");
html.append("<td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>모델명</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + modelName + "</td></tr>");
html.append("<tr><td ").append(TD_HEADER).append(">작성자</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(writerName)).append("</td>");
html.append("<td ").append(TD_HEADER).append(">모델명</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(modelName)).append("</td></tr>");
if(!currencyName.isEmpty()){
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>통화</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + currencyName + "</td>");
html.append("<td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>환율</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + exchangeRate + "</td></tr>");
html.append("<tr><td ").append(TD_HEADER).append(">통화</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(currencyName)).append("</td>");
html.append("<td ").append(TD_HEADER).append(">환율</td>");
html.append("<td ").append(TD_VALUE).append(">").append(escapeHtml(exchangeRate)).append("</td></tr>");
}
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>합계금액</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;' colspan='3'>");
html.append("<tr><td ").append(TD_HEADER).append(">합계금액</td>");
html.append("<td ").append(TD_VALUE).append(" colspan='3'>");
if(!totalAmountKrw.isEmpty() && !"0".equals(totalAmountKrw)){
html.append(totalAmountKrw + " (KRW)");
if(!totalAmount.isEmpty()) html.append(" / " + totalAmount + " (" + currencyName + ")");
html.append(escapeHtml(totalAmountKrw)).append(" (KRW)");
if(!totalAmount.isEmpty()) html.append(" / ").append(escapeHtml(totalAmount)).append(" (").append(escapeHtml(currencyName)).append(")");
} else {
html.append(totalAmount);
html.append(escapeHtml(totalAmount));
}
html.append("</td></tr>");
if(!validityPeriod.isEmpty()){
html.append("<tr><td style='border:1px solid #333;background-color:#D9E2F3;font-weight:bold;font-size:9pt;'>유효기간</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;' colspan='3'>" + validityPeriod + "</td></tr>");
}
html.append("</table>");
// 품목 리스트
if(itemList != null && !itemList.isEmpty()){
html.append("<table cellspacing='0' cellpadding='4' style='border-collapse:collapse;width:100%;'>");
html.append("<table cellspacing='0' cellpadding='0' style='border-collapse:collapse;width:100%;margin-bottom:5px;'>");
html.append("<thead><tr>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>No</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>품명</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>규격</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>수량</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>단위</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>단가</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>금액</th>");
html.append("<th style='border:1px solid #333;background-color:#4472C4;color:#fff;text-align:center;font-size:9pt;'>비고</th>");
html.append("<th ").append(TH_ITEM).append(">No</th>");
html.append("<th ").append(TH_ITEM).append(">품명</th>");
html.append("<th ").append(TH_ITEM).append(">규격</th>");
html.append("<th ").append(TH_ITEM).append(">수량</th>");
html.append("<th ").append(TH_ITEM).append(">단위</th>");
html.append("<th ").append(TH_ITEM).append(">단가</th>");
html.append("<th ").append(TH_ITEM).append(">금액</th>");
html.append("<th ").append(TH_ITEM).append(">비고</th>");
html.append("</tr></thead><tbody>");
int no = 1;
for(Map item : itemList){
item = CommonUtils.toUpperCaseMapKey(item);
html.append("<tr>");
html.append("<td style='border:1px solid #333;text-align:center;font-size:9pt;'>" + no++ + "</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + CommonUtils.checkNull(item.get("DESCRIPTION")) + "</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + CommonUtils.checkNull(item.get("SPECIFICATION")) + "</td>");
html.append("<td style='border:1px solid #333;text-align:right;font-size:9pt;'>" + CommonUtils.checkNull(item.get("QUANTITY")) + "</td>");
html.append("<td style='border:1px solid #333;text-align:center;font-size:9pt;'>" + CommonUtils.checkNull(item.get("UNIT")) + "</td>");
html.append("<td style='border:1px solid #333;text-align:right;font-size:9pt;'>" + CommonUtils.checkNull(item.get("UNIT_PRICE")) + "</td>");
html.append("<td style='border:1px solid #333;text-align:right;font-size:9pt;'>" + CommonUtils.checkNull(item.get("AMOUNT")) + "</td>");
html.append("<td style='border:1px solid #333;font-size:9pt;'>" + CommonUtils.checkNull(item.get("REMARK")) + "</td>");
html.append("<td ").append(TD_ITEM).append(" style='border:1px solid #333;text-align:center;font-size:9pt;padding:4px;'>").append(no++).append("</td>");
html.append("<td ").append(TD_ITEM).append(">").append(escapeHtml(CommonUtils.checkNull(item.get("DESCRIPTION")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(">").append(escapeHtml(CommonUtils.checkNull(item.get("SPECIFICATION")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(" style='border:1px solid #333;text-align:right;font-size:9pt;padding:4px;'>").append(escapeHtml(CommonUtils.checkNull(item.get("QUANTITY")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(" style='border:1px solid #333;text-align:center;font-size:9pt;padding:4px;'>").append(escapeHtml(CommonUtils.checkNull(item.get("UNIT")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(" style='border:1px solid #333;text-align:right;font-size:9pt;padding:4px;'>").append(escapeHtml(CommonUtils.checkNull(item.get("UNIT_PRICE")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(" style='border:1px solid #333;text-align:right;font-size:9pt;padding:4px;'>").append(escapeHtml(CommonUtils.checkNull(item.get("AMOUNT")))).append("</td>");
html.append("<td ").append(TD_ITEM).append(">").append(escapeHtml(CommonUtils.checkNull(item.get("REMARK")))).append("</td>");
html.append("</tr>");
}
html.append("</tbody></table>");
}
// 비고 (테이블 내 비고)
if(!noteRemarks.isEmpty()){
html.append("<br/><div><strong>비고:</strong><br/>" + noteRemarks.replace("\n", "<br/>") + "</div>");
html.append("<div style='margin-top:8px;padding:6px;border:1px solid #999;font-size:9pt;'>");
html.append("<strong>&lt;비고&gt;</strong><br/>").append(noteRemarks.replace("\n", "<br/>"));
html.append("</div>");
}
// 양식별 하단 섹션
if("2".equals(templateType)){
// 장비견적서: NOTES_CONTENT (HTML 형식)
if(!notesContent.isEmpty()){
html.append("<div style='margin-top:8px;padding:6px;border:1px solid #999;font-size:9pt;line-height:1.8;'>");
html.append(notesContent);
html.append("</div>");
}
} else {
// 일반견적서: NOTE1~NOTE4 (참조사항)
boolean hasNotes = !note1.isEmpty() || !note2.isEmpty() || !note3.isEmpty() || !note4.isEmpty();
if(hasNotes){
html.append("<div style='margin-top:8px;padding:6px;border:1px solid #999;font-size:9pt;'>");
html.append("<strong>&lt;참조사항&gt;</strong><br/>");
if(!note1.isEmpty()) html.append(escapeHtml(note1)).append("<br/>");
if(!note2.isEmpty()) html.append(escapeHtml(note2)).append("<br/>");
if(!note3.isEmpty()) html.append(escapeHtml(note3)).append("<br/>");
if(!note4.isEmpty()) html.append(escapeHtml(note4));
html.append("</div>");
}
}
// 담당자 정보
if(!managerName.isEmpty() || !managerContact.isEmpty()){
html.append("<div style='margin-top:8px;text-align:right;font-size:9pt;'>");
if(!managerName.isEmpty()) html.append("담당자 : ").append(escapeHtml(managerName));
if(!managerContact.isEmpty()) html.append("<br/>연락처 : ").append(escapeHtml(managerContact));
html.append("</div>");
}
html.append("</div>");