견적서 엑셀다운로드 API 및 파일삭제 API 추가

This commit is contained in:
LAPTOP-L3VE7KK2\USER 2024-11-07 10:54:08 +09:00
parent 164690a987
commit 1e4b68b7b0
17 changed files with 373 additions and 99 deletions

36
pom.xml
View File

@ -125,6 +125,42 @@
<artifactId>ini4j</artifactId>
<version>0.5.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-scratchpad -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jxls -->
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>net.sf.jxls</groupId>
<artifactId>jxls-core</artifactId>
<version>1.0.6</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-jexcel</artifactId>
<version>1.0.9</version>
</dependency>
</dependencies>
<build>

View File

@ -3,6 +3,8 @@ package com.interplug.qcast.biz.estimate;
import com.interplug.qcast.biz.estimate.dto.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@ -61,4 +63,15 @@ public class EstimateController {
throws Exception {
return estimateService.insertEstimateCopy(estimateRequest);
}
@Operation(description = "견적서를 엑셀로 다운로드한다.")
@PostMapping("/excel-download")
@ResponseStatus(HttpStatus.OK)
public void excelDownload(
HttpServletRequest request,
HttpServletResponse response,
@RequestBody EstimateRequest estimateRequest)
throws Exception {
estimateService.excelDownload(request, response, estimateRequest);
}
}

View File

@ -10,6 +10,9 @@ public interface EstimateMapper {
// 견적서 상세 확인
public EstimateResponse selectEstimateDetail(EstimateRequest estimateRequest);
// 견적서 PDF 상세 확인
public EstimateResponse selectEstimatePdfDetail(EstimateRequest estimateRequest);
// 견적서 아이템 목록 조회
public List<ItemResponse> selectEstimateItemList(EstimateRequest estimateRequest);

View File

@ -12,11 +12,19 @@ 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.ExcelUtil;
import com.interplug.qcast.util.InterfaceQsp;
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.util.ArrayList;
import java.util.List;
import java.util.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@ -36,6 +44,9 @@ public class EstimateService {
@Value("${qsp.url}")
private String QSP_API_URL;
@Value("${file.excel.template.path}")
private String excelTemplateFilePath;
private final ObjectMapper objectMapper;
private final EstimateMapper estimateMapper;
@ -157,7 +168,7 @@ public class EstimateService {
response.setItemList(estimateItemList);
// 합산금액 계산
this.selectTotalPriceInfo(response);
this.selectTotalPriceInfo(response, estimateItemList, "");
// 첨부파일 목록 조회
FileRequest fileDeleteReq = new FileRequest();
@ -522,7 +533,103 @@ public class EstimateService {
return response;
}
public void selectTotalPriceInfo(EstimateResponse estimateResponse) 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<NoteResponse> noteList = new ArrayList<NoteResponse>();
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);
}
// 아이템 목록 조회
List<ItemResponse> 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++) : "");
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())));
String excelFileName = "Quation_Detail";
String excelTemplatePath =
excelTemplateFilePath + File.separator + "excel_download_quotation_detail_template.xlsx";
excelUtil.download(
request,
response,
this.convertVoToMap(estimateResponse),
this.convertListToMap(estimateItemList),
excelFileName,
excelTemplatePath);
} catch (Exception e) {
e.printStackTrace();
}
}
public void selectTotalPriceInfo(
EstimateResponse estimateResponse, List<ItemResponse> itemList, String unitPriceFlg)
throws Exception {
BigDecimal totAmount = BigDecimal.ZERO;
BigDecimal totVol = BigDecimal.ZERO;
BigDecimal pkgTotPrice = BigDecimal.ZERO;
@ -531,7 +638,6 @@ public class EstimateService {
BigDecimal totPrice = BigDecimal.ZERO;
String estimateType = estimateResponse.getEstimateType();
List<ItemResponse> itemList = estimateResponse.getItemList();
// 주택패키지 단가
BigDecimal pkgAsp =
@ -544,9 +650,20 @@ public class EstimateService {
new BigDecimal(
StringUtils.isEmpty(itemResponse.getAmount()) ? "0" : itemResponse.getAmount());
// 판매가
BigDecimal salePrice =
new BigDecimal(
StringUtils.isEmpty(itemResponse.getSalePrice()) ? "0" : itemResponse.getSalePrice());
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(
@ -590,10 +707,55 @@ public class EstimateService {
estimateResponse.setPkgTotPrice(String.valueOf(pkgTotPrice));
estimateResponse.setTotAmount(String.valueOf(totAmount.setScale(0, BigDecimal.ROUND_HALF_UP)));
estimateResponse.setTotVol(String.valueOf(totVol.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)));
}
public Map<String, Object> convertVoToMap(Object vo) {
Map<String, Object> result = new HashMap<String, Object>();
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;
}
public static <T> List<Map<String, Object>> convertListToMap(Collection<T> target) {
List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
for (T element : target) {
Map<String, Object> resultMap = new HashMap<String, Object>();
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;
}
}

View File

@ -1,85 +0,0 @@
package com.interplug.qcast.biz.estimate.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;
import lombok.Data;
@Data
public class EstimatePdfResponse {
@Schema(description = "물건번호")
private String objectNo;
@Schema(description = "플랜번호")
private String planNo;
@Schema(description = "판매점ID")
private String saleStoreId;
@Schema(description = "견적서 유효기간")
private String estimateValidityTerm;
@Schema(description = "견적갱신일")
private String estimateDate;
@Schema(description = "PKG 단가")
private String pkgAsp;
@Schema(description = "비고")
private String remarks;
// 가격 관련 정보
@Schema(description = "총 수량")
private String totAmount;
@Schema(description = "총 용량")
private String totVol;
@Schema(description = "PKG 총액")
private String pkgTotPrice;
@Schema(description = "공급가액")
private String supplyPrice;
@Schema(description = "부가세")
private String vatPrice;
@Schema(description = "총액")
private String totPrice;
// 물건정보 정보
@Schema(description = "물건명")
private String objectName;
@Schema(description = "경칭")
private String objectNameOmit;
// 판매점 정보
@Schema(description = "고객 판매점명")
private String custSaleStoreName;
@Schema(description = "판매점명")
private String saleStoreName;
@Schema(description = "우편번호")
private String zipNo;
@Schema(description = "주소")
private String address;
@Schema(description = "전화번호")
private String tel;
@Schema(description = "Fax번호")
private String fax;
// PKG 정보
@Schema(description = "주택패키지 여부")
private String pkgYn;
@Schema(description = "주택패키지 번호")
private String pkgNo;
// 데이터 목록 관련 정보
@Schema(description = "아이템 목록")
List<ItemResponse> itemList;
}

View File

@ -156,6 +156,19 @@ public class EstimateRequest {
@Schema(description = "아이템번호 목록")
private String[] arrItemId;
// 다운로드 관련 조건
@Schema(description = "다운로드 정가 표시여부")
private String schUnitPriceFlg;
@Schema(description = "다운로드 표시여부")
private String schDisplayFlg;
@Schema(description = "가대중량표 포함 여부")
private String schWeightFlg;
@Schema(description = "도면 포함 여부")
private String schDrawingFlg;
// 데이터 목록 관련 정보
@Schema(description = "지붕재 목록")
List<RoofRequest> roofList;

View File

@ -149,6 +149,9 @@ public class EstimateResponse {
@Schema(description = "총 용량")
private String totVol;
@Schema(description = "총 용량 (Kw)")
private String totVolKw;
@Schema(description = "PKG 총액")
private String pkgTotPrice;
@ -180,10 +183,45 @@ public class EstimateResponse {
@Schema(description = "판매점 레벨")
private String saleStoreLevel;
// 판매점 정보
@Schema(description = "고객 판매점명")
private String custSaleStoreName;
@Schema(description = "고객 경칭")
private String custOmit;
@Schema(description = "판매점명")
private String saleStoreName;
@Schema(description = "우편번호")
private String zipNo;
@Schema(description = "주소")
private String address;
@Schema(description = "전화번호")
private String tel;
@Schema(description = "Fax번호")
private String fax;
@Schema(description = "법인번호")
private String bizNo;
// PKG 정보
@Schema(description = "주택패키지 여부")
private String pkgYn;
@Schema(description = "주택패키지 번호")
private String pkgNo;
// 데이터 목록 관련 정보
@Schema(description = "아이템 목록")
List<ItemResponse> itemList;
@Schema(description = "견적서 특이사항 목록")
List<NoteResponse> noteList;
@Schema(description = "첨부파일 목록")
List<FileResponse> fileList;
}

View File

@ -14,6 +14,9 @@ public class ItemResponse {
@Schema(description = "플랜번호")
private String planNo;
@Schema(description = "번호")
private String no;
@Schema(description = "아이템 ID")
private String itemId;
@ -35,6 +38,9 @@ public class ItemResponse {
@Schema(description = "단가")
private String salePrice;
@Schema(description = "정가")
private String unitPrice;
@Schema(description = "합산")
private String saleTotPrice;

View File

@ -10,4 +10,7 @@ public class NoteRequest {
@Schema(description = "검색 - 특이사항 타입 코드")
private String schSpnTypeCd;
@Schema(description = "검색 - 특이사항 코드 목록")
private String[] arrSpnAttrCd;
}

View File

@ -55,4 +55,12 @@ public class FileController {
Integer resultCnt = fileService.setFile(saveFileList, null);
return resultCnt;
}
@Operation(description = "파일을 삭제 한다.")
@PostMapping("/fileDelete")
@ResponseStatus(HttpStatus.NO_CONTENT)
public Integer fileDelete(@RequestBody FileRequest fileRequest) throws Exception {
Integer resultCnt = fileService.deleteFile(fileRequest);
return resultCnt;
}
}

View File

@ -301,4 +301,13 @@ public class FileService {
// zip 파일로 변환
zipFileManager.createZipFile(response, strZipFileName, listFile);
}
public Integer deleteFile(FileRequest fileRequest) throws Exception {
Integer iResult = 0;
// 첨부파일 논리적 삭제
iResult = fileMapper.deleteFile(fileRequest); // 파일 삭제
return iResult;
}
}

View File

@ -0,0 +1,55 @@
package com.interplug.qcast.util;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.jxls.exception.ParsePropertyException;
import org.jxls.common.Context;
import org.jxls.util.JxlsHelper;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RequiredArgsConstructor
public class ExcelUtil {
/**
* jxls을 이용한 엑셀다운로드
*
* @param request HttpServletRequest
* @param response HttpServletResponse
* @param map 엑셀 출력데이터
* @param list 엑셀 출력 목록 데이터
* @param fileName 다운로드 파일명
* @param templateFilePath 템플릿 파일경로
*/
public void download(
HttpServletRequest request,
HttpServletResponse response,
Map<String, Object> map,
List<Map<String, Object>> list,
String fileName,
String templateFilePath)
throws ParsePropertyException, InvalidFormatException {
try {
InputStream is = new BufferedInputStream(new FileInputStream(templateFilePath));
response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + ".xlsx\"");
try (OutputStream os = response.getOutputStream()) {
Context context = new Context();
context.putVar("data", map);
context.putVar("list", list);
JxlsHelper.getInstance().processTemplate(is, os, context);
}
} catch (Exception e) {
log.debug(e.getMessage());
}
}
}

View File

@ -45,4 +45,5 @@ qsp:
file:
root.path: C:\\
ini.root.path: C:\\NewEstimate
ini.base.filename: 料金シミュレーション.ini
ini.base.filename: 料金シミュレーション.ini
excel.template.path: C:\\qcastjp\template

View File

@ -46,4 +46,5 @@ qsp:
file:
root.path: C:\\
ini.root.path: C:\\NewEstimate
ini.base.filename: 料金シミュレーション.ini
ini.base.filename: 料金シミュレーション.ini
excel.template.path: C:\\qcastjp\\template

View File

@ -46,4 +46,5 @@ qsp:
file:
root.path: C:\\
ini.root.path: \\10.31.0.21\NewEstimate
ini.base.filename: 料金シミュレーション.ini
ini.base.filename: 料金シミュレーション.ini
excel.template.path: C:\\qcastjp\\template

View File

@ -65,7 +65,7 @@
AND P.DEL_FLG = '0'
</select>
<select id="selectEstimatePdfDetail" parameterType="com.interplug.qcast.biz.estimate.dto.EstimateRequest" resultType="com.interplug.qcast.biz.estimate.dto.EstimatePdfResponse">
<select id="selectEstimatePdfDetail" parameterType="com.interplug.qcast.biz.estimate.dto.EstimateRequest" resultType="com.interplug.qcast.biz.estimate.dto.EstimateResponse">
/* sqlid : com.interplug.qcast.biz.estimate.selectPdfEstimateDetail */
SELECT
T.*
@ -75,12 +75,16 @@
, SS2.ADDRESS
, SS2.TEL
, SS2.FAX
, SS2.BIZ_NO
FROM
(
SELECT
P.OBJECT_NO
, P.PLAN_NO
, P.ESTIMATE_VALIDITY_TERM
, P.ESTIMATE_TYPE
, P.ESTIMATE_OPTION
, P.PKG_ASP
, P.REMARKS
, CONVERT(NVARCHAR(10), P.LAST_EDIT_DATETIME, 121) AS ESTIMATE_DATE
, O.SALE_STORE_ID
@ -162,6 +166,12 @@
<if test='schSpnTypeCd != null and schSpnTypeCd != ""'>
AND OSN.SPN_TYPE_CD = #{schSpnTypeCd}
</if>
<if test='arrSpnAttrCd != null'>
AND CL.CODE IN
<foreach collection="arrSpnAttrCd" item="code" index="index" separator="," open="(" close=")">
#{code}
</foreach>
</if>
</select>
<select id="selectEstimateNoteItemList" parameterType="com.interplug.qcast.biz.estimate.dto.NoteRequest" resultType="com.interplug.qcast.biz.estimate.dto.NoteResponse">

View File

@ -44,7 +44,7 @@
<if test="planNo != null and planNo != ''">
AND U.PLAN_NO = #{planNo}
</if>
<if test='(planNo == null or planNo != "" ) and planNoNullChkFlg == "1"'>
<if test='(planNo == null or planNo == "" ) and planNoNullChkFlg == "1"'>
AND U.PLAN_NO IS NULL
</if>
<if test="no != null and no != ''">