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.config.Exception.ErrorCode; import com.interplug.qcast.config.Exception.QcastException; import com.interplug.qcast.config.message.Messages; import com.interplug.qcast.util.InterfaceQsp; import io.micrometer.common.util.StringUtils; import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; 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; private final ObjectMapper objectMapper; private final EstimateMapper estimateMapper; private final FileMapper fileMapper; public PriceResponse 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.getDocTpCd())) { throw new QcastException( ErrorCode.INVALID_INPUT_VALUE, message.getMessage("common.message.required.data", "Estimate Type")); } PriceResponse 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("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, PriceResponse.class); } else { // [msg] No data throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } return response; } public PriceResponse 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")); } PriceResponse 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, PriceResponse.class); } else { // [msg] No data throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data")); } return response; } 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); // 첨부파일 목록 조회 FileRequest fileDeleteReq = new FileRequest(); fileDeleteReq.setObjectNo(objectNo); fileDeleteReq.setCategory("10"); fileDeleteReq.setPlanNo(planNo); List fileList = fileMapper.selectFileList(fileDeleteReq); response.setFileList(fileList); } return response; } 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 itemList = estimateRequest.getItemList(); // 도면 작성일 경우에만 지붕재 데이터를 셋팅 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]. 지붕재 관련 데이터 셋팅 List roofList = estimateRequest.getRoofList(); // 지붕재 아이템 ID String roofMaterialIds = ""; // 지붕재 공법 ID String supportMethodIds = ""; // 지붕재 시공사양 ID String constructSpecifications = ""; // 지붕재 아이템명 String roofMaterialIdMultis = ""; // 지붕재 공법명 String supportMethodIdMultis = ""; // 가대 메이커명 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.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.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); 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; } } } // [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) { estimateOptions += !StringUtils.isEmpty(estimateOptions) ? splitStr : ""; estimateOptions += noteResponse.getCode(); } estimateRequest.setEstimateOption(estimateOptions); } // 아아템 목록 필수 값 체크 BigDecimal capacity = BigDecimal.ZERO; String moduleModel = ""; String pcTypeNo = ""; for (ItemRequest itemRequest : itemList) { 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); // 견적서 모든 아이템 제거 estimateMapper.deleteEstimateItemList(estimateRequest); // 견적서 아이템 신규 추가 for (ItemRequest itemRequest : itemList) { itemRequest.setObjectNo(estimateRequest.getObjectNo()); itemRequest.setPlanNo(estimateRequest.getPlanNo()); 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); } } public void 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")); } // [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); } // 도면 복사 (추후 개발 필요) } public void selectTotalPriceInfo(EstimateResponse estimateResponse) throws Exception { BigDecimal totAmount = BigDecimal.ZERO; BigDecimal totVol = BigDecimal.ZERO; BigDecimal supplyPrice = BigDecimal.ZERO; BigDecimal vatPrice = BigDecimal.ZERO; BigDecimal totPrice = BigDecimal.ZERO; String estimateType = estimateResponse.getEstimateType(); List itemList = estimateResponse.getItemList(); // 주택패키지 단가 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 = 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))); // 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)) { supplyPrice.add(pkgAsp.multiply(totVol)); } // 부가세 계산 (10프로) vatPrice = supplyPrice.multiply(new BigDecimal("0.01")); // 총 가격 합산 (공급가 + 부가세) totPrice = totPrice.add(supplyPrice.add(vatPrice)); estimateResponse.setTotAmount(String.valueOf(totAmount.setScale(0, BigDecimal.ROUND_HALF_UP))); estimateResponse.setTotVol(String.valueOf(totVol.setScale(0, BigDecimal.ROUND_HALF_UP))); 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))); } }