package com.interplug.qcast.biz.estimate; import com.fasterxml.jackson.databind.DeserializationFeature; import com.interplug.qcast.biz.estimate.dto.*; import com.interplug.qcast.biz.file.FileMapper; import com.interplug.qcast.biz.file.dto.FileRequest; import com.interplug.qcast.biz.file.dto.FileResponse; import com.interplug.qcast.biz.object.ObjectMapper; 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.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; import org.springframework.stereotype.Service; import org.springframework.web.util.UriComponentsBuilder; @Slf4j @Service @RequiredArgsConstructor public class EstimateService { private final InterfaceQsp interfaceQsp; @Autowired Messages message; @Value("${qsp.url}") private String QSP_API_URL; @Value("${file.excel.template.path}") private String excelTemplateFilePath; private final ObjectMapper objectMapper; private final EstimateMapper estimateMapper; private final FileMapper fileMapper; @Autowired private PwrGnrSimService pwrGnrSimService; /** * QSP 1차점 price 관리 목록 조회 * * @param priceRequest 1차점 price 관련 정보 * @return PriceResponse 1차점 price 목록 * @throws Exception */ public EstimateApiResponse selectStorePriceList(PriceRequest priceRequest) throws Exception { // Validation if (StringUtils.isEmpty(priceRequest.getSaleStoreId())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sale Store ID")); } if (StringUtils.isEmpty(priceRequest.getSapSalesStoreCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sap Sale Store Code")); } if (StringUtils.isEmpty(priceRequest.getDocTpCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Estimate Type")); } EstimateApiResponse response = null; /* [1]. QSP API (url + param) Setting */ String url = QSP_API_URL + "/api/price/storePriceList"; String apiUrl = UriComponentsBuilder.fromHttpUrl(url) .queryParam("saleStoreId", priceRequest.getSaleStoreId()) .queryParam("sapSalesStoreCd", priceRequest.getSapSalesStoreCd()) .queryParam("docTpCd", priceRequest.getDocTpCd()) .build() .toUriString(); /* [2]. QSP API CALL -> Response */ String strResponse = interfaceQsp.callApi(HttpMethod.GET, apiUrl, null); 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, EstimateApiResponse.class); } else { // [msg] No data throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } return response; } /** * QSP 아이템 가격 목록 조회 * * @param priceRequest QSP 관련 아이템 목록 정보 * @return PriceResponse QSP 아이템 가격 목록 * @throws Exception */ public EstimateApiResponse selectItemPriceList(PriceRequest priceRequest) throws Exception { // Validation if (StringUtils.isEmpty(priceRequest.getSaleStoreId())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sale Store ID")); } if (StringUtils.isEmpty(priceRequest.getSapSalesStoreCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sap Sale Store Code")); } if (StringUtils.isEmpty(priceRequest.getPriceCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Price Code")); } EstimateApiResponse response = null; /* [1]. QSP API CALL -> Response */ String strResponse = interfaceQsp.callApi( HttpMethod.POST, QSP_API_URL + "/api//price/storePriceItemList", priceRequest); 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, EstimateApiResponse.class); } else { // [msg] No data throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } return response; } /** * 견적 특이사항 목록 조회 * * @param noteRequest 견적 특이사항 조회 정보 * @return List 견적 특이사항 목록 * @throws Exception */ public List selectSpecialNoteList(NoteRequest noteRequest) throws Exception { return estimateMapper.selectEstimateNoteList(noteRequest); } /** * 견적서 상세 조회 * * @param objectNo 물건번호 * @param planNo 플랜번호 * @return EstimateResponse 견적서 상세 정보 * @throws Exception */ public EstimateResponse selectEstimateDetail(String objectNo, String planNo) throws Exception { // Validation if (StringUtils.isEmpty(objectNo)) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Object No")); } if (StringUtils.isEmpty(planNo)) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Plan No")); } EstimateRequest estimateRequest = new EstimateRequest(); estimateRequest.setObjectNo(objectNo); estimateRequest.setPlanNo(planNo); // 견적서 상세 조회 EstimateResponse response = estimateMapper.selectEstimateDetail(estimateRequest); if (response == null) { throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } else { // 아이템 목록 조회 List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest); response.setItemList(estimateItemList); // 총 합산금액 계산 this.selectTotalPriceInfo(response, estimateItemList, ""); // 첨부파일 목록 조회 FileRequest fileDeleteReq = new FileRequest(); fileDeleteReq.setObjectNo(objectNo); fileDeleteReq.setCategory("10"); fileDeleteReq.setPlanNo(planNo); List fileList = fileMapper.selectFileList(fileDeleteReq); response.setFileList(fileList); } return response; } /** * 견적서 저장 * * @param estimateRequest 견적서 저장 정보 * @throws Exception */ public void insertEstimate(EstimateRequest estimateRequest) throws Exception { // Validation if (StringUtils.isEmpty(estimateRequest.getObjectNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Object No")); } if (StringUtils.isEmpty(estimateRequest.getPlanNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Plan No")); } if (StringUtils.isEmpty(estimateRequest.getSaleStoreId())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sale Store ID")); } if (StringUtils.isEmpty(estimateRequest.getSapSalesStoreCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Sap Sale Store Code")); } String splitStr = "、"; List roofList = new ArrayList(); List itemList = estimateRequest.getItemList(); try { // 도면 작성일 경우에만 지붕재 데이터를 셋팅 if ("1".equals(estimateRequest.getDrawingFlg())) { // [1]. 견적서 기본셋팅 estimateRequest.setEstimateType("YJOD"); estimateRequest.setPriceCd("UNIT_PRICE"); // 물건정보 조회 후 데이터 축출 ObjectResponse objectResponse = objectMapper.selectObjectDetail(estimateRequest.getObjectNo()); if (objectResponse != null) { estimateRequest.setWeatherPoint( objectResponse.getPrefName() + " - " + objectResponse.getAreaName()); estimateRequest.setCharger(objectResponse.getReceiveUser()); } // [2]. 지붕재 관련 데이터 셋팅 roofList = estimateRequest.getRoofList(); // 지붕재 시공사양 ID String constructSpecifications = ""; // 지붕재 아이템 ID String roofMaterialIds = ""; // 지붕재 공법 ID String supportMethodIds = ""; // 지붕재 아이템명 String roofMaterialIdMultis = ""; // 지붕재 공법명 String supportMethodIdMultis = ""; // 지붕재 시공사양명 String constructSpecificationMultis = ""; // 가대 메이커명 String supportMeakers = ""; for (RoofRequest roofRequest : roofList) { if (!StringUtils.isEmpty(roofRequest.getRoofMaterialId())) { roofMaterialIds += !StringUtils.isEmpty(roofMaterialIds) ? splitStr : ""; roofMaterialIds += roofRequest.getRoofMaterialId(); } if (!StringUtils.isEmpty(roofRequest.getSupportMethodId())) { supportMethodIds += !StringUtils.isEmpty(supportMethodIds) ? splitStr : ""; supportMethodIds += roofRequest.getSupportMethodId(); } if (!StringUtils.isEmpty(roofRequest.getConstructSpecification())) { constructSpecifications += !StringUtils.isEmpty(constructSpecifications) ? splitStr : ""; constructSpecifications += roofRequest.getConstructSpecification(); } if (!StringUtils.isEmpty(roofRequest.getRoofMaterialIdMulti())) { roofMaterialIdMultis += !StringUtils.isEmpty(roofMaterialIdMultis) ? splitStr : ""; roofMaterialIdMultis += roofRequest.getRoofMaterialIdMulti(); } if (!StringUtils.isEmpty(roofRequest.getSupportMethodIdMulti())) { supportMethodIdMultis += !StringUtils.isEmpty(supportMethodIdMultis) ? splitStr : ""; supportMethodIdMultis += roofRequest.getSupportMethodIdMulti(); } if (!StringUtils.isEmpty(roofRequest.getConstructSpecificationMulti())) { constructSpecificationMultis += !StringUtils.isEmpty(constructSpecificationMultis) ? splitStr : ""; constructSpecificationMultis += roofRequest.getConstructSpecificationMulti(); } if (!StringUtils.isEmpty(roofRequest.getSupportMeaker())) { supportMeakers += !StringUtils.isEmpty(supportMeakers) ? splitStr : ""; supportMeakers += roofRequest.getSupportMeaker(); } } // 지붕재 관련 데이터 셋팅 estimateRequest.setRoofMaterialId(roofMaterialIds); estimateRequest.setSupportMethodId(supportMethodIds); estimateRequest.setConstructSpecification(constructSpecifications); estimateRequest.setRoofMaterialIdMulti(roofMaterialIdMultis); estimateRequest.setSupportMethodIdMulti(supportMethodIdMultis); estimateRequest.setConstructSpecificationMulti(constructSpecificationMultis); estimateRequest.setSupportMeaker(supportMeakers); // [3]. 아이템 관련 데이터 셋팅 String[] arrItemId = new String[itemList.size()]; int i = 0; for (ItemRequest itemRequest : itemList) { arrItemId[i++] = itemRequest.getItemId(); } estimateRequest.setArrItemId(arrItemId); // 아이템의 마스터 정보 및 정가 정보 조회 List itemResponseList = estimateMapper.selectItemMasterList(estimateRequest); // BOM 정보 목록 List estimateBomList = new ArrayList(); int j = 1; for (ItemRequest itemRequest : itemList) { int dispOrder = 100 * j++; itemRequest.setDispOrder(String.valueOf(dispOrder)); for (ItemResponse itemResponse : itemResponseList) { if (itemRequest.getItemId().equals(itemResponse.getItemId())) { itemRequest.setItemNo(itemResponse.getItemNo()); itemRequest.setItemName(itemResponse.getItemName()); itemRequest.setUnit(itemResponse.getUnit()); itemRequest.setPnowW(itemResponse.getPnowW()); itemRequest.setSpecification(itemResponse.getPnowW()); itemRequest.setUnitPrice(itemResponse.getSalePrice()); itemRequest.setSalePrice(itemResponse.getSalePrice()); itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg()); itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg()); itemRequest.setItemGroup(itemResponse.getItemGroup()); itemRequest.setItemCtgGr(itemResponse.getItemCtgGr()); itemRequest.setPartAdd("0"); itemRequest.setDelFlg("0"); break; } } // 아이템 BOM Header인 경우 컴포넌트 등록 처리 if ("ERLA".equals(itemRequest.getItemCtgGr())) { List itemBomList = estimateMapper.selectItemMasterBomList(itemRequest.getItemId()); int k = 1; for (ItemResponse itemResponse : itemBomList) { ItemRequest bomItem = new ItemRequest(); bomItem.setPaDispOrder(String.valueOf(dispOrder)); bomItem.setDispOrder(String.valueOf(dispOrder + k++)); bomItem.setItemId(itemResponse.getItemId()); bomItem.setItemNo(itemResponse.getItemNo()); bomItem.setItemName(itemResponse.getItemName()); bomItem.setUnit(itemResponse.getUnit()); bomItem.setPnowW(itemResponse.getPnowW()); bomItem.setSpecification(itemResponse.getPnowW()); bomItem.setAmount( String.valueOf( Integer.parseInt(itemResponse.getBomAmount()) * Integer.parseInt(itemRequest.getAmount()))); bomItem.setBomAmount(itemResponse.getBomAmount()); bomItem.setUnitPrice(itemResponse.getSalePrice()); bomItem.setSalePrice(itemResponse.getSalePrice()); bomItem.setFileUploadFlg(itemResponse.getFileUploadFlg()); bomItem.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg()); bomItem.setItemGroup(itemResponse.getItemGroup()); bomItem.setItemCtgGr(itemResponse.getItemCtgGr()); bomItem.setPartAdd("0"); bomItem.setDelFlg("0"); estimateBomList.add(bomItem); } } } // BOM 컴포넌트 추가 for (ItemRequest estimateBom : estimateBomList) { itemList.add(estimateBom); } // [4]. 견적특이사항 관련 데이터 셋팅 NoteRequest noteRequest = new NoteRequest(); noteRequest.setArrItemId(arrItemId); noteRequest.setSchSpnTypeCd("COMM"); List noteList = estimateMapper.selectEstimateNoteList(noteRequest); noteRequest.setSchSpnTypeCd("PROD"); List noteItemList = estimateMapper.selectEstimateNoteItemList(noteRequest); // 견적특이사항 코드 String estimateOptions = ""; for (NoteResponse noteResponse : noteList) { if ("ATTR001".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR001"; // 공통 필수체크 } else if ("ATTR002".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR002"; // YJOD 필수체크 } else if ("ATTR003".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR003"; // 출력제어시간 기본 체크 } else if ("ATTR004".equals(noteResponse.getCode()) && "1".equals(estimateRequest.getNorthArrangement())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR004"; // 북면설치 체크 } else if ("ATTR005".equals(noteResponse.getCode()) && "1".equals(objectResponse.getSaltAreaFlg())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR005"; // 염해지역 체크 } else if ("ATTR006".equals(objectResponse.getColdRegionFlg()) && "1".equals(estimateRequest.getNorthArrangement())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR006"; // 적설지역 체크 } else if ("ATTR007".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR007"; // 지붕재치수, 레이아웃 기본 체크 } else if ("ATTR008".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR008"; // 별도비용 기본 체크 } else if ("ATTR009".equals(noteResponse.getCode())) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += "ATTR009"; // 과적재 기본 체크 } } for (NoteResponse noteResponse : noteItemList) { if (estimateOptions.indexOf(noteResponse.getCode()) < 0) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += noteResponse.getCode(); } } estimateRequest.setEstimateOption(estimateOptions); // 아이템별 특이사항 코드 체크 for (ItemRequest itemRequest : itemList) { String specialNoteCd = ""; for (NoteResponse noteResponse : noteItemList) { if (itemRequest.getItemId().equals(noteResponse.getItemId())) { specialNoteCd += !StringUtils.isEmpty(specialNoteCd) ? splitStr : ""; specialNoteCd += noteResponse.getCode(); } } itemRequest.setSpecialNoteCd(specialNoteCd); } } // 아이템 목록 필수 값 체크 BigDecimal capacity = BigDecimal.ZERO; String moduleModel = ""; String pcTypeNo = ""; for (ItemRequest itemRequest : itemList) { if (StringUtils.isEmpty(itemRequest.getDispOrder())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Display Order")); } if (StringUtils.isEmpty(itemRequest.getItemId())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Item ID")); } if (StringUtils.isEmpty(itemRequest.getAmount())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Item Amount")); } // 수량 BigDecimal amount = new BigDecimal( StringUtils.isEmpty(itemRequest.getAmount()) ? "0" : itemRequest.getAmount()); // 아이템용량 BigDecimal pnowW = new BigDecimal( StringUtils.isEmpty(itemRequest.getPnowW()) ? "0" : itemRequest.getPnowW()); // 모듈/PC 체크 if ("MODULE_".equals(itemRequest.getItemGroup())) { moduleModel += !StringUtils.isEmpty(moduleModel) ? splitStr : ""; moduleModel += itemRequest.getItemNo(); capacity = capacity.add(amount.multiply(pnowW)); } else if ("PC_".equals(itemRequest.getItemGroup())) { pcTypeNo += !StringUtils.isEmpty(pcTypeNo) ? splitStr : ""; pcTypeNo += itemRequest.getItemNo(); } } estimateRequest.setCapacity(String.valueOf(capacity)); estimateRequest.setModuleModel(moduleModel); estimateRequest.setPcTypeNo(pcTypeNo); // 물건정보 수정 if (!StringUtils.isEmpty(estimateRequest.getObjectName()) || !StringUtils.isEmpty(estimateRequest.getSurfaceType()) || !StringUtils.isEmpty(estimateRequest.getSetupHeight()) || !StringUtils.isEmpty(estimateRequest.getStandardWindSpeedId()) || !StringUtils.isEmpty(estimateRequest.getSnowfall())) { estimateMapper.updateObject(estimateRequest); } // 견적서 정보 수정 estimateRequest.setPriceCd("UNIT_PRICE"); estimateMapper.updateEstimate(estimateRequest); // 도면 작성일 경우에만 지붕재 데이터 초기화 후 저장 if ("1".equals(estimateRequest.getDrawingFlg())) { // 견적서 지붕면/아이템 제거 estimateMapper.deleteEstimateRoofList(estimateRequest); estimateMapper.deleteEstimateRoofItemList(estimateRequest); // 견적서 지붕면/아이템 신규 추가 for (RoofRequest roofRequest : roofList) { roofRequest.setObjectNo(estimateRequest.getObjectNo()); roofRequest.setPlanNo(estimateRequest.getPlanNo()); roofRequest.setUserId(estimateRequest.getUserId()); estimateMapper.insertEstimateRoof(roofRequest); List roofItemList = roofRequest.getRoofItemList(); for (ItemRequest itemRequest : roofItemList) { itemRequest.setObjectNo(estimateRequest.getObjectNo()); itemRequest.setPlanNo(estimateRequest.getPlanNo()); itemRequest.setRoofNo(roofRequest.getRoofNo()); estimateMapper.insertEstimateRoofItem(itemRequest); } } } // 견적서 모든 아이템 제거 estimateMapper.deleteEstimateItemList(estimateRequest); // 견적서 아이템 신규 추가 String hisNo = estimateMapper.selectEstimateItemHisNo(estimateRequest); // 아이템 히스토리 번호 조회 for (ItemRequest itemRequest : itemList) { itemRequest.setHisNo(hisNo); itemRequest.setObjectNo(estimateRequest.getObjectNo()); itemRequest.setPlanNo(estimateRequest.getPlanNo()); itemRequest.setBomAmount( !StringUtils.isEmpty(itemRequest.getBomAmount()) ? itemRequest.getBomAmount() : "0"); itemRequest.setPartAdd( !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0"); itemRequest.setItemChangeFlg( !StringUtils.isEmpty(itemRequest.getItemChangeFlg()) ? itemRequest.getItemChangeFlg() : "0"); itemRequest.setUserId(estimateRequest.getUserId()); estimateMapper.insertEstimateItemHis(itemRequest); if (!"1".equals(itemRequest.getDelFlg())) { estimateMapper.insertEstimateItem(itemRequest); } } // QSP Q.CAST SEND API List resultList = new ArrayList(); resultList = this.sendEstimateApi(estimateRequest); // API에서 받은 문서번호 업데이트 for (EstimateSendResponse result : resultList) { estimateRequest.setObjectNo(result.getObjectNo()); estimateRequest.setPlanNo(result.getPlanNo()); estimateRequest.setDocNo(result.getDocNo()); estimateRequest.setSyncFlg(result.getSyncFlg()); estimateMapper.updateEstimateApi(estimateRequest); } } catch (Exception e) { e.printStackTrace(); } } /** * 견적서 복사 * * @param estimateRequest 견적서 복사 정보 * @return EstimateResponse 견적서 복사 후 상세 정보 * @throws Exception */ public EstimateResponse insertEstimateCopy(EstimateRequest estimateRequest) throws Exception { // Validation if (StringUtils.isEmpty(estimateRequest.getObjectNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Object No")); } if (StringUtils.isEmpty(estimateRequest.getPlanNo())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Plan No")); } EstimateResponse response = new EstimateResponse(); // [1]. 총 플랜 목록 조회 및 제약조건 처리 (플랜 10개까지만 등록) PlanRequest planRequest = new PlanRequest(); planRequest.setObjectNo(estimateRequest.getObjectNo()); List planList = objectMapper.selectPlanList(planRequest); if (planList.size() > 9) { throw new QcastException( ErrorCode.INTERNAL_SERVER_ERROR, message.getMessage("common.message.plan.save.limit")); } List itemList = new ArrayList(); List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest); for (ItemResponse itemResponse : estimateItemList) { ItemRequest itemRequest = new ItemRequest(); itemRequest.setItemId(itemResponse.getItemId()); itemRequest.setAmount(itemResponse.getAmount()); itemList.add(itemRequest); } // [2]. 아이템 관련 데이터 셋팅 (복사 시 정가 셋팅) String[] arrItemId = new String[itemList.size()]; int i = 0; for (ItemRequest itemRequest : itemList) { arrItemId[i++] = itemRequest.getItemId(); } estimateRequest.setArrItemId(arrItemId); // 아이템의 마스터 정보 및 정가 정보 조회 List itemResponseList = estimateMapper.selectItemMasterList(estimateRequest); int j = 1; for (ItemRequest itemRequest : itemList) { itemRequest.setDispOrder(String.valueOf(j++)); for (ItemResponse itemResponse : itemResponseList) { if (itemRequest.getItemId().equals(itemResponse.getItemId())) { itemRequest.setItemNo(itemResponse.getItemNo()); itemRequest.setItemName(itemResponse.getItemName()); itemRequest.setUnit(itemResponse.getUnit()); itemRequest.setPnowW(itemResponse.getPnowW()); itemRequest.setSpecification(itemResponse.getPnowW()); itemRequest.setUnitPrice(itemResponse.getSalePrice()); itemRequest.setSalePrice(itemResponse.getSalePrice()); itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg()); itemRequest.setItemGroup(itemResponse.getItemGroup()); break; } } } // 견적서 복사 estimateMapper.insertEstimateCopy(estimateRequest); // 견적서 아이템 복사 for (ItemRequest itemRequest : itemList) { itemRequest.setObjectNo(estimateRequest.getObjectNo()); itemRequest.setPlanNo(estimateRequest.getCopyPlanNo()); itemRequest.setPartAdd( !StringUtils.isEmpty(itemRequest.getPartAdd()) ? itemRequest.getPartAdd() : "0"); itemRequest.setItemChangeFlg( !StringUtils.isEmpty(itemRequest.getItemChangeFlg()) ? itemRequest.getItemChangeFlg() : "0"); itemRequest.setUserId(estimateRequest.getUserId()); estimateMapper.insertEstimateItem(itemRequest); } // 도면 복사 (추후 개발 필요) // 리턴 response.setObjectNo(estimateRequest.getObjectNo()); response.setPlanNo(estimateRequest.getCopyPlanNo()); return response; } /** * 견적서 엑셀 다운로드 * * @param request HttpServletRequest * @param response HttpServletResponse * @param estimateRequest 견적서 엑셀 다운로드 요청 정보 * @throws Exception */ public void excelDownload( HttpServletRequest request, HttpServletResponse response, EstimateRequest estimateRequest) throws Exception { ExcelUtil excelUtil = new ExcelUtil(); EstimateResponse estimateResponse = new EstimateResponse(); String splitStr = "、"; try { // 견적서 상세 조회 estimateResponse = estimateMapper.selectEstimatePdfDetail(estimateRequest); if ("1".equals(estimateRequest.getSchDisplayFlg())) { estimateResponse.setCustSaleStoreName(estimateResponse.getObjectName()); estimateResponse.setCustOmit(estimateResponse.getObjectNameOmit()); } else { estimateResponse.setCustOmit("様邸"); } // 견적서 특이사항 조회 List noteList = new ArrayList(); if (!StringUtils.isEmpty(estimateResponse.getEstimateOption())) { String[] arrSpnAttrCd = new String[estimateResponse.getEstimateOption().split(splitStr).length]; int i = 0; for (String str : estimateResponse.getEstimateOption().split(splitStr)) { arrSpnAttrCd[i++] = str; } NoteRequest noteRequest = new NoteRequest(); noteRequest.setArrSpnAttrCd(arrSpnAttrCd); noteList = estimateMapper.selectEstimateNoteList(noteRequest); estimateResponse.setNoteList(noteList); } // 지붕재 목록 조회 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); // 아이템 목록 조회 List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest); // 총 합산금액 계산 this.selectTotalPriceInfo( estimateResponse, estimateItemList, estimateRequest.getSchUnitPriceFlg()); int j = 1; for (ItemResponse itemResponse : estimateItemList) { itemResponse.setNo(String.valueOf(j++)); // 문자열 통화로 변환 처리 itemResponse.setSalePrice( String.format("%1$,.0f", Double.parseDouble(itemResponse.getSalePrice()))); itemResponse.setAmount( String.format("%1$,.0f", Double.parseDouble(itemResponse.getAmount()))); itemResponse.setSaleTotPrice( String.format("%1$,.0f", Double.parseDouble(itemResponse.getSaleTotPrice()))); if ("YJSS".equals(estimateResponse.getEstimateType()) && !"1".equals(itemResponse.getPkgMaterialFlg())) { itemResponse.setSalePrice(""); itemResponse.setSaleTotPrice(""); } } // 합산 문자열 통화로 변환 처리 estimateResponse.setPkgYn("YJSS".equals(estimateResponse.getEstimateType()) ? "Y" : "N"); estimateResponse.setPkgNo( "YJSS".equals(estimateResponse.getEstimateType()) ? String.valueOf(j++) : ""); if ("YJSS".equals(estimateResponse.getEstimateType())) { estimateResponse.setPkgAsp( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getPkgAsp()))); } estimateResponse.setTotVol( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getTotVol()))); estimateResponse.setPkgTotPrice( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getPkgTotPrice()))); estimateResponse.setSupplyPrice( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getSupplyPrice()))); estimateResponse.setVatPrice( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getVatPrice()))); estimateResponse.setTotPrice( String.format("%1$,.0f", Double.parseDouble(estimateResponse.getTotPrice()))); // 발전시뮬레이션 계산 PwrGnrSimRequest pwrGnrSimRequest = new PwrGnrSimRequest(); pwrGnrSimRequest.setObjectNo(estimateResponse.getObjectNo()); pwrGnrSimRequest.setPlanNo(estimateResponse.getPlanNo()); PwrGnrSimResponse pwrGnrSimResponse = pwrGnrSimService.selectPwrGnrSimulation(pwrGnrSimRequest); if (pwrGnrSimResponse != null) { try { // 발전시뮬레이션 안내사항 조회 PwrGnrSimGuideResponse pwrGnrSimGuideInfo = pwrGnrSimService.selectPwrGnrSimulationGuideInfo(); if (pwrGnrSimGuideInfo != null) { pwrGnrSimResponse.setGuideInfo(pwrGnrSimGuideInfo.getData()); } } catch (Exception e) { } } estimateResponse.setPwrGnrSim(pwrGnrSimResponse); if ("PDF".equals(estimateRequest.getSchDownload())) { // PDF 다운로드 String[] arrSection = new String[5]; // 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; arrSection[iSection] = "div.section1"; iSection++; arrSection[iSection] = "div.section2"; iSection++; arrSection[iSection] = "div.section4"; iSection++; arrSection[iSection] = "div.section5"; iSection++; // 발전시뮬레이션 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(); } } /** * 아이템 목록을 통한 견적서 가격 자동계산 * * @param estimateResponse 견적서 정보 * @param itemList 아이템 정보 목록 * @param unitPriceFlg 가격 표시 코드 * @throws Exception */ public void selectTotalPriceInfo( EstimateResponse estimateResponse, List itemList, String unitPriceFlg) throws Exception { BigDecimal totAmount = BigDecimal.ZERO; BigDecimal totVol = BigDecimal.ZERO; BigDecimal pkgTotPrice = BigDecimal.ZERO; BigDecimal supplyPrice = BigDecimal.ZERO; BigDecimal vatPrice = BigDecimal.ZERO; BigDecimal totPrice = BigDecimal.ZERO; String estimateType = estimateResponse.getEstimateType(); // 주택패키지 단가 BigDecimal pkgAsp = new BigDecimal( StringUtils.isEmpty(estimateResponse.getPkgAsp()) ? "0" : estimateResponse.getPkgAsp()); for (ItemResponse itemResponse : itemList) { // 수량 BigDecimal amount = new BigDecimal( StringUtils.isEmpty(itemResponse.getAmount()) ? "0" : itemResponse.getAmount()); // 판매가 BigDecimal salePrice = BigDecimal.ZERO; if ("1".equals(unitPriceFlg)) { salePrice = new BigDecimal( StringUtils.isEmpty(itemResponse.getUnitPrice()) ? "0" : itemResponse.getUnitPrice()); } else { salePrice = new BigDecimal( StringUtils.isEmpty(itemResponse.getSalePrice()) ? "0" : itemResponse.getSalePrice()); } // 아이템용량 BigDecimal pnowW = new BigDecimal( StringUtils.isEmpty(itemResponse.getPnowW()) ? "0" : itemResponse.getPnowW()); // 아이템 단가 합산 itemResponse.setSaleTotPrice(String.valueOf(salePrice.multiply(amount))); // 컴포넌트는 제외하고 계산 if (StringUtils.isEmpty(itemResponse.getPaDispOrder())) { // YJSS인 경우 (PKG 단가 * 모듈용량) + 패키지 제외상품 총 합산 // YJOD인 경우 모든 아이템의 총 합산 if ("YJSS".equals(estimateType)) { if ("1".equals(itemResponse.getPkgMaterialFlg())) { // 패키지 제외상품 여부(1) supplyPrice = supplyPrice.add(salePrice.multiply(amount)); } else { if ("1".equals(itemResponse.getModuleFlg())) { totVol = totVol.add(pnowW.multiply(amount)); } } } else { if ("1".equals(itemResponse.getModuleFlg())) { totVol = totVol.add(pnowW.multiply(amount)); } supplyPrice = supplyPrice.add(salePrice.multiply(amount)); } // 주문수량 더하기 totAmount = totAmount.add(amount); } } if ("YJSS".equals(estimateType)) { pkgTotPrice = pkgAsp.multiply(totVol); pkgTotPrice = pkgTotPrice.setScale(0, BigDecimal.ROUND_HALF_UP); supplyPrice = supplyPrice.add(pkgTotPrice); } // 부가세 계산 (10프로) vatPrice = supplyPrice.multiply(new BigDecimal("0.1")); // 총 가격 합산 (공급가 + 부가세) totPrice = totPrice.add(supplyPrice.add(vatPrice)); estimateResponse.setPkgTotPrice(String.valueOf(pkgTotPrice)); estimateResponse.setTotAmount(String.valueOf(totAmount.setScale(0, BigDecimal.ROUND_HALF_UP))); estimateResponse.setTotVol(String.valueOf(totVol)); estimateResponse.setTotVolKw( String.valueOf( totVol.multiply(new BigDecimal("0.001")).setScale(3, BigDecimal.ROUND_FLOOR))); estimateResponse.setSupplyPrice( String.valueOf(supplyPrice.setScale(0, BigDecimal.ROUND_HALF_UP))); estimateResponse.setVatPrice(String.valueOf(vatPrice.setScale(0, BigDecimal.ROUND_HALF_UP))); estimateResponse.setTotPrice(String.valueOf(totPrice.setScale(0, BigDecimal.ROUND_HALF_UP))); } /** * QSP Q.CAST 견적서 전송 API * * @param estimateRequest 견적서 요청 정보 * @return String 문서번호 * @throws Exception */ public List sendEstimateApi(EstimateRequest estimateRequest) throws Exception { EstimateSendRequest estimateSendRequest = new EstimateSendRequest(); List quoteList = new ArrayList(); String docNo = ""; // 견적서 상세 조회 EstimateSendResponse estimateSendResponse = estimateMapper.selectEstimateApiDetail(estimateRequest); if (estimateSendResponse == null) { throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } else { estimateSendResponse.setSaveType("3"); estimateSendResponse.setSyncFlg("0"); estimateSendResponse.setConstructSpecification( !StringUtils.isEmpty(estimateRequest.getConstructSpecification()) ? estimateRequest.getConstructSpecification().split("、")[0] : ""); estimateSendResponse.setDelFlg("1".equals(estimateSendResponse.getDelFlg()) ? "Y" : "N"); // 아이템 목록 조회 List estimateItemList = estimateMapper.selectEstimateItemList(estimateRequest); estimateSendResponse.setItemList(estimateItemList); // 첨부파일 목록 조회 FileRequest fileDeleteReq = new FileRequest(); fileDeleteReq.setObjectNo(estimateRequest.getObjectNo()); fileDeleteReq.setPlanNo(estimateRequest.getPlanNo()); fileDeleteReq.setCategory("10"); List fileList = fileMapper.selectFileList(fileDeleteReq); estimateSendResponse.setFileList(fileList); quoteList.add(estimateSendResponse); estimateSendRequest.setQuoteList(quoteList); } EstimateApiResponse response = null; /* [1]. QSP API CALL -> Response */ String strResponse = interfaceQsp.callApi( HttpMethod.POST, QSP_API_URL + "/api/master/qcastQuotationSave", estimateSendRequest); 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, EstimateApiResponse.class); Map result = (Map) response.getData(); if (result != null) { List> succList = (List>) result.get("successList"); for (Map succ : succList) { if (succ != null) { for (EstimateSendResponse data : quoteList) { if (data.getObjectNo().equals(succ.get("objectNo")) && data.getPlanNo().equals(succ.get("planNo"))) { data.setSyncFlg("1"); data.setDocNo(String.valueOf(succ.get("docNo"))); } } } } } } else { log.error("sendEstimateApi >>> " + message.getMessage("common.message.no.data")); } return quoteList; } /** * Object => Map 변환 함수 * * @param vo Object * @return Map Map 변환 정보 */ public Map convertVoToMap(Object vo) { Map result = new HashMap(); try { BeanInfo info = Introspector.getBeanInfo(vo.getClass()); for (PropertyDescriptor pd : info.getPropertyDescriptors()) { Method reader = pd.getReadMethod(); if (reader != null) { result.put(pd.getName(), reader.invoke(vo)); } } } catch (Exception e) { log.error("convertVoToMap >>> " + e.getMessage()); } return result; } /** * List => List 변환 함수 * * @param target List * @return List> List 변환 정보 */ public static List> convertListToMap(Collection target) { List> resultList = new ArrayList>(); for (T element : target) { Map resultMap = new HashMap(); Field[] fieldList = element.getClass().getDeclaredFields(); if (fieldList != null && fieldList.length > 0) { try { for (int i = 0; i < fieldList.length; i++) { String curInsName = fieldList[i].getName(); Field field = element.getClass().getDeclaredField(curInsName); field.setAccessible(true); Object targetValue = field.get(element); resultMap.put(curInsName, targetValue); } resultList.add(resultMap); } catch (Exception e) { log.error("convertListToMap >>> " + e.getMessage()); } } } return resultList; } }