diff --git a/pom.xml b/pom.xml index 91ffd4b2..6e0e6a81 100644 --- a/pom.xml +++ b/pom.xml @@ -160,6 +160,32 @@ 1.0.9 + + + org.jsoup + jsoup + 1.18.1 + + + com.itextpdf + html2pdf + 4.0.3 + + + com.itextpdf + kernel + 7.2.5 + + + com.itextpdf + layout + 7.2.5 + + + com.itextpdf + io + 7.2.5 + diff --git a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java index 72814127..1a7dbc1b 100644 --- a/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java +++ b/src/main/java/com/interplug/qcast/biz/displayItem/dto/ItemResponse.java @@ -9,6 +9,9 @@ public class ItemResponse { @Schema(description = "Itme Id") private String itemId; + @Schema(description = "Item No") + private String itemNo; + @Schema(description = "Item Name") private String itemName; diff --git a/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java b/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java index 8fcc17b6..594c6392 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/EstimateMapper.java @@ -21,15 +21,27 @@ public interface EstimateMapper { // 아이템 마스터 목록 조회 public List selectItemMasterList(EstimateRequest estimateRequest); + // 견적서 지붕재 인증용량 조회 + public String selectEstimateRoofCertVolKw(EstimateRequest estimateRequest); + // 견적서 지붕재 목록 조회 public List selectEstimateRoofList(EstimateRequest estimateRequest); + // 견적서 지붕재 PC 목록 조회 + public List selectEstimateRoofPcList(EstimateRequest estimateRequest); + + // 견적서 지붕재 용량 목록 조회 + public List selectEstimateRoofVolList(EstimateRequest estimateRequest); + // 견적서 특이사항 목록 조회 public List selectEstimateNoteList(NoteRequest noteRequest); // 아이템 마스터 목록 조회 public List selectEstimateNoteItemList(NoteRequest noteRequest); + // 아이템 히스토리 번호 조회 + public String selectEstimateItemHisNo(EstimateRequest estimateRequest); + // 물건정보 수정 public int updateObject(EstimateRequest estimateRequest); @@ -48,6 +60,9 @@ public interface EstimateMapper { // 견적서 아이템 등록 public int insertEstimateItem(ItemRequest itemRequest); + // 견적서 아이템 히스토리 등록 + public int insertEstimateItemHis(ItemRequest itemRequest); + // 견적서 지붕재 목록 삭제(물리 삭제) public int deleteEstimateRoofList(EstimateRequest estimateRequest); diff --git a/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java b/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java index fb0f62a0..f5ac681f 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/EstimateService.java @@ -10,26 +10,31 @@ import com.interplug.qcast.biz.object.dto.ObjectResponse; import com.interplug.qcast.biz.object.dto.PlanRequest; import com.interplug.qcast.biz.object.dto.PlanResponse; import com.interplug.qcast.biz.pwrGnrSimulation.PwrGnrSimService; +import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimGuideResponse; import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimRequest; +import com.interplug.qcast.biz.pwrGnrSimulation.dto.PwrGnrSimResponse; import com.interplug.qcast.config.Exception.ErrorCode; import com.interplug.qcast.config.Exception.QcastException; import com.interplug.qcast.config.message.Messages; import com.interplug.qcast.util.ExcelUtil; import com.interplug.qcast.util.InterfaceQsp; +import com.interplug.qcast.util.PdfUtil; import io.micrometer.common.util.StringUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.text.SimpleDateFormat; import java.util.*; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpMethod; @@ -357,6 +362,8 @@ public class EstimateService { itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg()); itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg()); itemRequest.setItemGroup(itemResponse.getItemGroup()); + itemRequest.setPartAdd("0"); + itemRequest.setDelFlg("0"); break; } } @@ -508,11 +515,13 @@ public class EstimateService { estimateMapper.deleteEstimateItemList(estimateRequest); // 견적서 아이템 신규 추가 - int j = 1; + String hisNo = estimateMapper.selectEstimateItemHisNo(estimateRequest); // 아이템 히스토리 번호 조회 + int j = 1, k = 1; for (ItemRequest itemRequest : itemList) { + itemRequest.setHisNo(hisNo); itemRequest.setObjectNo(estimateRequest.getObjectNo()); itemRequest.setPlanNo(estimateRequest.getPlanNo()); - itemRequest.setDispOrder(String.valueOf(j++)); + itemRequest.setDispOrder(String.valueOf(k++)); itemRequest.setPartAdd( !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0"); itemRequest.setItemChangeFlg( @@ -521,7 +530,13 @@ public class EstimateService { : "0"); itemRequest.setUserId(estimateRequest.getUserId()); - estimateMapper.insertEstimateItem(itemRequest); + estimateMapper.insertEstimateItemHis(itemRequest); + + if (!"1".equals(itemRequest.getDelFlg())) { + itemRequest.setDispOrder(String.valueOf(j++)); + + estimateMapper.insertEstimateItem(itemRequest); + } } // QSP Q.CAST SEND API @@ -685,7 +700,30 @@ public class EstimateService { // 지붕재 목록 조회 RoofInfoResponse roofInfoResponse = new RoofInfoResponse(); List roofList = estimateMapper.selectEstimateRoofList(estimateRequest); + List roofPcList = estimateMapper.selectEstimateRoofPcList(estimateRequest); + estimateRequest.setSchItemGroup("MODULE_"); + List roofVolList = estimateMapper.selectEstimateRoofVolList(estimateRequest); + + BigDecimal moduleTotAmount = BigDecimal.ZERO; + BigDecimal moduleTotVolKw = BigDecimal.ZERO; + for (RoofResponse roofVol : roofVolList) { + BigDecimal amount = + new BigDecimal(StringUtils.isEmpty(roofVol.getAmount()) ? "0" : roofVol.getAmount()); + BigDecimal vol = + new BigDecimal(StringUtils.isEmpty(roofVol.getVolKw()) ? "0" : roofVol.getVolKw()); + + moduleTotAmount = moduleTotAmount.add(amount); + moduleTotVolKw = moduleTotVolKw.add(vol); + } + roofInfoResponse.setModuleTotAmount(String.valueOf(moduleTotAmount)); + roofInfoResponse.setModuleTotVolKw(String.valueOf(moduleTotVolKw)); + roofInfoResponse.setRoofList(roofList); + roofInfoResponse.setRoofPcList(roofPcList); + roofInfoResponse.setRoofVolList(roofVolList); + + // 인증용량 구하기 (지붕면마다 모듈과 PCS의 총 용량을 서로 비교해 낮은쪽 용량으로 합산) + roofInfoResponse.setCertVolKw(estimateMapper.selectEstimateRoofCertVolKw(estimateRequest)); estimateResponse.setRoofInfo(roofInfoResponse); @@ -734,19 +772,14 @@ public class EstimateService { estimateResponse.setTotPrice( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getTotPrice()))); - String excelFileName = "Quation_Detail"; - String excelTemplatePath = - excelTemplateFilePath + File.separator + "excel_download_quotation_detail_template.xlsx"; - // 발전시뮬레이션 계산 PwrGnrSimRequest pwrGnrSimRequest = new PwrGnrSimRequest(); pwrGnrSimRequest.setObjectNo(estimateResponse.getObjectNo()); pwrGnrSimRequest.setPlanNo(estimateResponse.getPlanNo()); - /* + PwrGnrSimResponse pwrGnrSimResponse = pwrGnrSimService.selectPwrGnrSimulation(pwrGnrSimRequest); if (pwrGnrSimResponse != null) { - try { // 발전시뮬레이션 안내사항 조회 PwrGnrSimGuideResponse pwrGnrSimGuideInfo = @@ -755,20 +788,52 @@ public class EstimateService { pwrGnrSimResponse.setGuideInfo(pwrGnrSimGuideInfo.getData()); } } catch (Exception e) { - } } estimateResponse.setPwrGnrSim(pwrGnrSimResponse); - */ - excelUtil.download( - request, - response, - this.convertVoToMap(estimateResponse), - this.convertListToMap(estimateItemList), - excelFileName, - excelTemplatePath); + if (true) { // PDF 다운로드 + String[] arrSection = new String[3]; // TODO Section 갯수 넣기 + int iSection = 0; + + String pdfFileName = + "Quation_Detail_" + new SimpleDateFormat("yyyyMMdd").format(new Date()); + String templateFilePath = "pdf_download_quotation_detail_template.html"; + + // 템플릿 html 조회 + Document doc = PdfUtil.getPdfDoc(request, templateFilePath); + + // 삭제하려는 element + Element elm; + + // 발전시뮬레이션 pdf Html 생성 + if (true) { + arrSection[iSection] = "div.section3"; + iSection++; + doc = pwrGnrSimService.pwrGnrSimPdfHtml(doc, pwrGnrSimResponse); + } else { + elm = doc.getElementsByClass("section3").first(); + elm.remove(); + } + + // pdf 다운로드 + PdfUtil.pdfDownload(request, response, doc, pdfFileName, arrSection); + + } else { + + String excelFileName = + "Quation_Detail_" + new SimpleDateFormat("yyyyMMdd").format(new Date()); + String excelTemplateNam = "excel_download_quotation_detail_template.xlsx"; + + excelUtil.download( + request, + response, + this.convertVoToMap(estimateResponse), + this.convertListToMap(estimateItemList), + excelFileName, + excelTemplateNam); + } } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/interplug/qcast/biz/estimate/dto/EstimateRequest.java b/src/main/java/com/interplug/qcast/biz/estimate/dto/EstimateRequest.java index daee7689..55950d59 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/dto/EstimateRequest.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/dto/EstimateRequest.java @@ -163,18 +163,21 @@ public class EstimateRequest { private String[] arrItemId; // 다운로드 관련 조건 - @Schema(description = "다운로드 정가 표시여부") + @Schema(description = "검색 - 다운로드 정가 표시여부") private String schUnitPriceFlg; - @Schema(description = "다운로드 표시여부") + @Schema(description = "검색 - 다운로드 표시여부") private String schDisplayFlg; - @Schema(description = "가대중량표 포함 여부") + @Schema(description = "검색 - 가대중량표 포함 여부") private String schWeightFlg; - @Schema(description = "도면 포함 여부") + @Schema(description = "검색 - 도면 포함 여부") private String schDrawingFlg; + @Schema(description = "검색 - 아이템 그룹") + private String schItemGroup; + // 데이터 목록 관련 정보 @Schema(description = "지붕재 목록") List roofList; diff --git a/src/main/java/com/interplug/qcast/biz/estimate/dto/ItemRequest.java b/src/main/java/com/interplug/qcast/biz/estimate/dto/ItemRequest.java index 560a7c27..fe6ebe19 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/dto/ItemRequest.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/dto/ItemRequest.java @@ -71,6 +71,12 @@ public class ItemRequest { @Schema(description = "아이템 그룹코드") private String itemGroup; + @Schema(description = "히스토리 번호") + private String hisNo; + + @Schema(description = "삭제여부") + private String delFlg; + @Schema(description = "사용자아이디") private String userId; } diff --git a/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofInfoResponse.java b/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofInfoResponse.java index 3f090e04..4c035da5 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofInfoResponse.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofInfoResponse.java @@ -9,6 +9,15 @@ import lombok.Setter; @Getter @Setter public class RoofInfoResponse { + @Schema(description = "인증용량") + private String certVolKw; + + @Schema(description = "모듈 총 수") + private String moduleTotAmount; + + @Schema(description = "모듈 총 용량") + private String moduleTotVolKw; + @Schema(description = "지붕면 목록") private List roofList; diff --git a/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofResponse.java b/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofResponse.java index e4d131b2..361fc215 100644 --- a/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofResponse.java +++ b/src/main/java/com/interplug/qcast/biz/estimate/dto/RoofResponse.java @@ -64,4 +64,19 @@ public class RoofResponse { @Schema(description = "설치높이") private String setupHeight; + + @Schema(description = "아이템 ID") + private String itemId; + + @Schema(description = "아이템 번호") + private String itemNo; + + @Schema(description = "매수") + private String amount; + + @Schema(description = "용량") + private String volKw; + + @Schema(description = "Pc 모듈 매수") + private String pcModuleAmount; } diff --git a/src/main/java/com/interplug/qcast/biz/object/ObjectService.java b/src/main/java/com/interplug/qcast/biz/object/ObjectService.java index bc28e492..a0e9e5b5 100644 --- a/src/main/java/com/interplug/qcast/biz/object/ObjectService.java +++ b/src/main/java/com/interplug/qcast/biz/object/ObjectService.java @@ -337,8 +337,41 @@ public class ObjectService { message.getMessage("common.message.required.data", "Object No")); } - // 물건정보 삭제 - result = objectMapper.deleteObject(objectRequest); + // object 상세 정보 조회 + ObjectResponse objectResponse = objectMapper.selectObjectDetail(objectRequest.getObjectNo()); + if (objectResponse != null) { + // 설계의뢰 번호가 존재하고 물건정보에 견적서가 없을 경우 QSP 설계의뢰 물건번호 초기화 + if (!StringUtils.isEmpty(objectResponse.getPlanReqNo()) + && objectResponse.getEstimateTotCnt() == 0) { + PlanReqResponse response = null; + + PlanReqRequest planReqRequest = new PlanReqRequest(); + planReqRequest.setSaleStoreId(objectResponse.getSaleStoreId()); + planReqRequest.setSaleStoreLevel(objectResponse.getSaleStoreLevel()); + planReqRequest.setObjectNo(objectResponse.getObjectNo()); + planReqRequest.setPlanReqNo(objectResponse.getPlanReqNo()); + planReqRequest.setDelFlg("1"); + + String strResponse = + interfaceQsp.callApi( + HttpMethod.POST, QSP_API_URL + "/api/planReq/updateObjectNo", planReqRequest); + if (!"".equals(strResponse)) { + com.fasterxml.jackson.databind.ObjectMapper om = + new com.fasterxml.jackson.databind.ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + response = om.readValue(strResponse, PlanReqResponse.class); + + Map map = (Map) response.getResult(); + if ("E".equals(String.valueOf(map.get("resultCode")))) { + throw new QcastException( + ErrorCode.INTERNAL_SERVER_ERROR, String.valueOf(map.get("resultMsg"))); + } + } + } + + // 물건정보 삭제 + result = objectMapper.deleteObject(objectRequest); + } return result; } diff --git a/src/main/java/com/interplug/qcast/biz/object/dto/ObjectResponse.java b/src/main/java/com/interplug/qcast/biz/object/dto/ObjectResponse.java index 072cd47c..a786e460 100644 --- a/src/main/java/com/interplug/qcast/biz/object/dto/ObjectResponse.java +++ b/src/main/java/com/interplug/qcast/biz/object/dto/ObjectResponse.java @@ -125,6 +125,9 @@ public class ObjectResponse { @Schema(description = "플랜 전체 건수") private String planTotCnt; + @Schema(description = "견적서 전체 건수") + private Integer estimateTotCnt; + // 플랜목록 @Schema(description = "플랜목록") private List planList; diff --git a/src/main/java/com/interplug/qcast/biz/object/dto/PlanReqRequest.java b/src/main/java/com/interplug/qcast/biz/object/dto/PlanReqRequest.java index a1aa3cd6..7898141c 100644 --- a/src/main/java/com/interplug/qcast/biz/object/dto/PlanReqRequest.java +++ b/src/main/java/com/interplug/qcast/biz/object/dto/PlanReqRequest.java @@ -17,6 +17,9 @@ public class PlanReqRequest { @Schema(description = "물건번호") private String objectNo; + @Schema(description = "삭제여부") + private String delFlg; + @Schema(description = "검색 - 설계의뢰번호") private String schPlanReqNo; diff --git a/src/main/java/com/interplug/qcast/biz/pwrGnrSimulation/PwrGnrSimService.java b/src/main/java/com/interplug/qcast/biz/pwrGnrSimulation/PwrGnrSimService.java index 7a2d1be5..7c4af5a5 100644 --- a/src/main/java/com/interplug/qcast/biz/pwrGnrSimulation/PwrGnrSimService.java +++ b/src/main/java/com/interplug/qcast/biz/pwrGnrSimulation/PwrGnrSimService.java @@ -7,19 +7,24 @@ import com.interplug.qcast.config.Exception.ErrorCode; import com.interplug.qcast.config.Exception.QcastException; import com.interplug.qcast.config.message.Messages; import com.interplug.qcast.util.InterfaceQsp; +import java.awt.*; import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; +import org.springframework.util.ObjectUtils; @Slf4j @Service @@ -1303,4 +1308,180 @@ public class PwrGnrSimService { return guideResponse; } + + public static Document pwrGnrSimPdfHtml(Document doc, PwrGnrSimResponse data) + throws IOException, QcastException { + Element elm; + + // 상단 요약정보 + elm = doc.getElementById("objectNo"); + elm.text( + ObjectUtils.nullSafeToString(data.getObjectNo() + " (Plan No : " + data.getPlanNo() + ")")); + + elm = doc.getElementById("drawingEstimateCreateDate"); + elm.text(ObjectUtils.nullSafeToString(data.getDrawingEstimateCreateDate())); + + elm = doc.getElementById("prefName"); + elm.text(ObjectUtils.nullSafeToString(data.getPrefName())); + + elm = doc.getElementById("areaName"); + elm.text(ObjectUtils.nullSafeToString(data.getAreaName())); + + elm = doc.getElementById("capacity"); + elm.text(ObjectUtils.nullSafeToString(data.getCapacity())); + + elm = doc.getElementById("anlFrcsGnrt"); + elm.text(ObjectUtils.nullSafeToString(data.getAnlFrcsGnrt())); + + elm = doc.getElementById("snowfall"); + elm.text(ObjectUtils.nullSafeToString(data.getSnowfall())); + + elm = doc.getElementById("standardWindSpeedId"); + elm.text(ObjectUtils.nullSafeToString(data.getStandardWindSpeedId())); + + if (data.getFrcPwrGnrList() != null && data.getFrcPwrGnrList().length > 0) { + + int[] onlyData = + Arrays.copyOfRange(data.getFrcPwrGnrList(), 0, data.getFrcPwrGnrList().length - 1); + + int referenceValue = 300; // table 높이 + int orgMaxValue = + Arrays.stream(onlyData) + .max() + .orElseThrow(() -> new QcastException(ErrorCode.INTERNAL_SERVER_ERROR, "배열이 없음")); + int maxValue = roundUpAuto(orgMaxValue / 8); + + // chart y축 + StringBuilder sb = new StringBuilder(); + for (int i = 8; i >= 1; i--) { + sb.append(""); + sb.append("") + .append(maxValue * i) + .append(""); + sb.append(""); + } + sb.append(""); + sb.append("0"); + elm = doc.getElementById("htmlY"); + elm.append(sb.toString()); + + // chart 데이터 + String[] color = { + "#B5D4F5", "#FFE899", "#FBC3AB", "#D1D1D1", "#FFE899", "#B5D0F0", "#C1E4B8", "#A3C9EE", + "#F7BBA2", "#BEBEBE", "#A8BBE0", "#B5D7A3" + }; + StringBuilder sb2 = new StringBuilder(); + for (int i = 0; i < onlyData.length; i++) { + double scaledValues = ((double) onlyData[i] / (maxValue * 8)) * referenceValue; + + sb2.append(""); + + sb2.append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("") + .append("
"); + + sb2.append( + "
"); + sb2.append(""); + } + elm = doc.getElementById("htmlX"); + elm.append(sb2.toString()); + } + + // 예측발전량 + if (data.getFrcPwrGnrList() != null && data.getFrcPwrGnrList().length > 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 13; i++) { + sb.append("" + ObjectUtils.nullSafeToString(data.getFrcPwrGnrList()[i]) + ""); + } + + elm = doc.getElementById("frcPwrGnrList_detail"); + elm.append(sb.toString()); + } + + // 모듈 list + if (data.getRoofModuleList() != null && data.getRoofModuleList().size() > 0) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < data.getRoofModuleList().size(); i++) { + PwrGnrSimRoofResponse listItem = data.getRoofModuleList().get(i); + + sb.append(""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getRoofSurface()) + ""); + sb.append( + "" + + Optional.ofNullable(listItem.getSlope()).orElse("") + + "" + + Optional.ofNullable(listItem.getAngle()).orElse("") + + ""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getAzimuth()) + ""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getItemNo()) + ""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getAmount()) + ""); + sb.append(""); + } + + elm = doc.getElementById("roofModuleList_detail"); + elm.append(sb.toString()); + } + + // pcs list + if (data.getPcsList() != null && data.getPcsList().size() > 0) { + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < data.getPcsList().size(); i++) { + PwrGnrSimRoofResponse listItem = data.getPcsList().get(i); + + sb.append(""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getItemNo()) + ""); + sb.append("" + ObjectUtils.nullSafeToString(listItem.getAmount()) + ""); + sb.append(""); + } + + elm = doc.getElementById("pcsList_detail"); + elm.append(sb.toString()); + } + + return doc; + } + + public static int roundUpAuto(double value) { + int length = (int) Math.log10(value) + 1; // 숫자의 자리수를 계산 + int place = (int) Math.pow(10, length - 1); // 자리수에 따른 올림 기준(10, 100, 1000 등) + + return (int) Math.ceil(value / place) * place; + } } diff --git a/src/main/java/com/interplug/qcast/util/ExcelUtil.java b/src/main/java/com/interplug/qcast/util/ExcelUtil.java index 66d5b424..90213d47 100644 --- a/src/main/java/com/interplug/qcast/util/ExcelUtil.java +++ b/src/main/java/com/interplug/qcast/util/ExcelUtil.java @@ -26,7 +26,9 @@ public class ExcelUtil { * @param map 엑셀 출력데이터 * @param list 엑셀 출력 목록 데이터 * @param fileName 다운로드 파일명 - * @param templateFilePath 템플릿 파일경로 + * @param templateFileName + * @throws ParsePropertyException + * @throws InvalidFormatException */ public void download( HttpServletRequest request, @@ -34,10 +36,15 @@ public class ExcelUtil { Map map, List> list, String fileName, - String templateFilePath) + String templateFileName) throws ParsePropertyException, InvalidFormatException { try { - InputStream is = new BufferedInputStream(new FileInputStream(templateFilePath)); + + String templateFilePath = "template/excel/" + templateFileName; + InputStream templateStream = + PdfUtil.class.getClassLoader().getResourceAsStream(templateFilePath); + + InputStream is = new BufferedInputStream(templateStream); response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xlsx\""); diff --git a/src/main/java/com/interplug/qcast/util/PdfUtil.java b/src/main/java/com/interplug/qcast/util/PdfUtil.java new file mode 100644 index 00000000..ae25eb62 --- /dev/null +++ b/src/main/java/com/interplug/qcast/util/PdfUtil.java @@ -0,0 +1,163 @@ +package com.interplug.qcast.util; + +import com.itextpdf.html2pdf.ConverterProperties; +import com.itextpdf.html2pdf.HtmlConverter; +import com.itextpdf.html2pdf.resolver.font.DefaultFontProvider; +import com.itextpdf.kernel.pdf.PdfDocument; +import com.itextpdf.kernel.pdf.PdfReader; +import com.itextpdf.kernel.pdf.PdfWriter; +import com.itextpdf.kernel.utils.PdfMerger; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PdfUtil { + // HTML 템플릿 파일 경로 + // @Value("${file.excel.template.path}") + // private String excelTemplateFilePath; + + /** + * 파일 템플릿 HTML 불러오기 + * + * @param request + * @param templateFileName + * @return + * @throws IOException + */ + public static Document getPdfDoc(HttpServletRequest request, String templateFileName) + throws IOException { + + String templateFilePath = "template/pdf/" + templateFileName; + InputStream templateStream = + PdfUtil.class.getClassLoader().getResourceAsStream(templateFilePath); + + if (templateStream == null) { + throw new FileNotFoundException("Template file not found: " + templateFilePath); + } + + Document doc = Jsoup.parse(templateStream, StandardCharsets.UTF_8.name(), ""); + + return doc; + } + + /** + * PDF 다운로드 + * + * @param request + * @param response + * @param doc + * @param pdfFileName 파일명 + * @param arrSection 노출 Section + * @throws IOException + */ + public static void pdfDownload( + HttpServletRequest request, + HttpServletResponse response, + Document doc, + String pdfFileName, + String[] arrSection) + throws IOException { + + // 응답에 PDF 설정 + response.setContentType("application/pdf"); + response.setHeader("Content-Disposition", "attachment; filename=\"" + pdfFileName + "\""); + + try (OutputStream os = response.getOutputStream(); + PdfWriter writer = new PdfWriter(os); + PdfDocument pdfDoc = new PdfDocument(writer)) { + + // 폰트 설정 + ConverterProperties properties = new ConverterProperties(); + String fontPath = "template/pdf/BIZUDPGothic-Regular.ttf"; + properties.setFontProvider(setFontProvider(fontPath)); + + // arrSection이 비어 있는 경우 HTML 전체를 변환 + if (arrSection == null || arrSection.length == 0) { + // HTML 전체를 pdfDoc에 직접 변환 + HtmlConverter.convertToPdf( + new ByteArrayInputStream(doc.html().getBytes(StandardCharsets.UTF_8)), os, properties); + os.flush(); + } else { + // arrSection이 있는 경우 각 섹션을 개별적으로 변환 후 병합 + String headHtml = doc.select("head").outerHtml(); + + PdfMerger merger = new PdfMerger(pdfDoc); + for (String section : arrSection) { + if (section != null && !"".equals(section)) { + Elements eSections = doc.select(section); + if (eSections != null && eSections.size() > 0) { + for (Element eSection : eSections) { + + String sectionHtml = + "" + headHtml + "" + eSection.outerHtml() + ""; + + try (ByteArrayOutputStream tempOutputStream = new ByteArrayOutputStream(); + PdfWriter tempWriter = new PdfWriter(tempOutputStream); + PdfDocument tempPdfDoc = new PdfDocument(tempWriter)) { + + HtmlConverter.convertToPdf( + new ByteArrayInputStream(sectionHtml.getBytes(StandardCharsets.UTF_8)), + tempPdfDoc, + properties); + + PdfDocument tempDoc = + new PdfDocument( + new PdfReader(new ByteArrayInputStream(tempOutputStream.toByteArray()))); + merger.merge(tempDoc, 1, tempDoc.getNumberOfPages()); + tempDoc.close(); + + if (!eSection.equals(eSections.last())) { + pdfDoc.addNewPage(); + } + } + } + } + } + } + pdfDoc.close(); + os.flush(); + } + } catch (Exception e) { + log.error("PDF 생성 중 오류 발생: " + e.getMessage(), e); + if (!response.isCommitted()) { + response.reset(); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "PDF 생성 중 오류가 발생했습니다."); + } + } + } + + /** + * 폰트 설정 메서드 + * + * @param fontPath 폰트 파일 경로 + * @return 설정된 FontProvider + */ + private static DefaultFontProvider setFontProvider(String fontPath) throws IOException { + DefaultFontProvider fontProvider = new DefaultFontProvider(false, false, false); + InputStream fontStream = PdfUtil.class.getClassLoader().getResourceAsStream(fontPath); + + if (fontStream != null) { + File tempFontFile = Files.createTempFile("BIZUDPGothic-Regular", ".ttf").toFile(); + Files.copy( + fontStream, tempFontFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); + fontProvider.addFont(tempFontFile.getAbsolutePath()); + fontStream.close(); + tempFontFile.deleteOnExit(); + } else { + log.error("폰트 파일을 찾을 수 없습니다: " + fontPath); + } + return fontProvider; + } +} diff --git a/src/main/resources/mappers/displayItem/displayItemMapper.xml b/src/main/resources/mappers/displayItem/displayItemMapper.xml index 96209c42..fc7118f9 100644 --- a/src/main/resources/mappers/displayItem/displayItemMapper.xml +++ b/src/main/resources/mappers/displayItem/displayItemMapper.xml @@ -50,6 +50,7 @@ FROM ( SELECT /* 전체 목록중 미표시 제품 */ MI.ITEM_ID + , MI.ITEM_NO , MI.ITEM_NAME , MI.DISP_ORDER FROM M_ITEM MI @@ -66,6 +67,7 @@ SELECT /* 미표시 제품 중 판매점만 표시 */ MI.ITEM_ID + , MI.ITEM_NO , MI.ITEM_NAME , MI.DISP_ORDER FROM M_SALES_STORE_DISP_ITEM SSDI @@ -84,10 +86,11 @@ /* sqlid : com.interplug.qcast.displayItem.getItemDetail */ SELECT MI.ITEM_ID + , MI.ITEM_NO , MI.ITEM_NAME , MI.GOODS_NO , MI.UNIT - , MI.SPECIFICATION + , MI.PNOW_W AS SPECIFICATION , MI.PNOW_W , MI.ITEM_GROUP , MI.MODULE_FLG diff --git a/src/main/resources/mappers/estimate/estimateMapper.xml b/src/main/resources/mappers/estimate/estimateMapper.xml index 48c295d1..0ce51d25 100644 --- a/src/main/resources/mappers/estimate/estimateMapper.xml +++ b/src/main/resources/mappers/estimate/estimateMapper.xml @@ -120,7 +120,7 @@ + + + + + + + + /* sqlid : com.interplug.qcast.biz.estimate.updateObject */ UPDATE T_OBJECT @@ -439,6 +569,56 @@ ) + + /* sqlid : com.interplug.qcast.biz.estimate.insertEstimateItemHits */ + INSERT INTO T_PART_ESTIMATE_HIS + ( + HIS_NO + , OBJECT_NO + , PLAN_NO + , DISP_ORDER + , ITEM_ID + , ITEM_NO + , ITEM_NAME + , UNIT + , AMOUNT + , UNIT_PRICE + , SALE_PRICE + + , SPECIAL_NOTE_CD + + , PKG_MATERIAL_FLG + , ITEM_CHANGE_FLG + , FILE_UPLOAD_FLG + , PART_ADD + , DEL_FLG + , CREATE_DATETIME + , CREATE_USER + ) VALUES ( + #{hisNo} + , #{objectNo} + , #{planNo} + , #{dispOrder} + , #{itemId} + , #{itemNo} + , #{itemName} + , #{unit} + , #{amount} + , #{unitPrice} + , #{salePrice} + + , #{specialNoteCd} + + , #{pkgMaterialFlg} + , #{itemChangeFlg} + , #{fileUploadFlg} + , #{partAdd} + , #{delFlg} + , GETDATE() + , #{userId} + ) + + /* sqlid : com.interplug.qcast.biz.estimate.insertEstimateRoof */ @@ -517,21 +697,21 @@ /* sqlid : com.interplug.qcast.biz.estimate.deleteEstimateRoofList */ DELETE FROM T_ROOF_ESTIMATE WHERE OBJECT_NO = #{objectNo} - AND PLAN_NO = #{planNo} + AND PLAN_NO = #{planNo} /* sqlid : com.interplug.qcast.biz.estimate.deleteEstimateRoofItemList */ DELETE FROM T_ROOF_ITEM_ESTIMATE WHERE OBJECT_NO = #{objectNo} - AND PLAN_NO = #{planNo} + AND PLAN_NO = #{planNo} /* sqlid : com.interplug.qcast.biz.estimate.deleteEstimateItemList */ DELETE FROM T_PART_ESTIMATE WHERE OBJECT_NO = #{objectNo} - AND PLAN_NO = #{planNo} + AND PLAN_NO = #{planNo} diff --git a/src/main/resources/mappers/file/fileMapper.xml b/src/main/resources/mappers/file/fileMapper.xml index 3f65ad61..b18361ce 100644 --- a/src/main/resources/mappers/file/fileMapper.xml +++ b/src/main/resources/mappers/file/fileMapper.xml @@ -7,6 +7,7 @@ SELECT TOP 1 U.OBJECT_NO + , U.PLAN_NO , U.NO , U.FAILE_NAME , U.CATEGORY diff --git a/src/main/resources/mappers/object/objectMapper.xml b/src/main/resources/mappers/object/objectMapper.xml index 783dc630..3d890072 100644 --- a/src/main/resources/mappers/object/objectMapper.xml +++ b/src/main/resources/mappers/object/objectMapper.xml @@ -307,6 +307,7 @@ , S.SALE_STORE_LEVEL , ISNULL(P.PREF_NAME, '') AS PREF_NAME , ISNULL(PA.AREA_NAME, '') AS AREA_NAME + , (SELECT COUNT(1) FROM T_PLAN WHERE OBJECT_NO = O.OBJECT_NO AND DEL_FLG = '0' AND DRAWING_ESTIMATE_CREATE_DATE IS NOT NULL) AS ESTIMATE_TOT_CNT FROM T_OBJECT O WITH (NOLOCK) INNER JOIN M_SALES_STORE S WITH(NOLOCK) ON O.SALE_STORE_ID = S.SALE_STORE_ID diff --git a/src/main/resources/template/excel/excel_download_quotation_detail_template.xlsx b/src/main/resources/template/excel/excel_download_quotation_detail_template.xlsx new file mode 100644 index 00000000..393ccca5 Binary files /dev/null and b/src/main/resources/template/excel/excel_download_quotation_detail_template.xlsx differ diff --git a/src/main/resources/template/pdf/BIZUDPGothic-Regular.ttf b/src/main/resources/template/pdf/BIZUDPGothic-Regular.ttf new file mode 100644 index 00000000..8c2bbdc2 Binary files /dev/null and b/src/main/resources/template/pdf/BIZUDPGothic-Regular.ttf differ diff --git a/src/main/resources/template/pdf/pdf_download_quotation_detail_template.html b/src/main/resources/template/pdf/pdf_download_quotation_detail_template.html new file mode 100644 index 00000000..d3210f56 --- /dev/null +++ b/src/main/resources/template/pdf/pdf_download_quotation_detail_template.html @@ -0,0 +1,255 @@ + + + + + + +
1111111111
+
222222222
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
物件番号作成日
都道府県日射量観測地点
システム容量年間予測発電量
積雪条件風速条件
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
1月2月3月4月5月6月7月8月9月10月11月12月
+
+
+ ● 予測発電量[kWh] +
+ + + + + + + + + + + + + + + + + + + + + + +
1月2月3月4月5月6月7月8月9月10月11月12月合計
+ + + + + + + + + + + + +
屋根面傾斜角方位角(度)太陽電池モジュール枚数
+ + + + + + + + + +
パワーコンディショナー
+
+ ● Hanwha Japan 年間発電量シミュレーション案内事項 +
+
+
+
+
+ + \ No newline at end of file