diff --git a/build.gradle b/build.gradle index 26e671c..ed843c1 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,10 @@ dependencies { implementation 'com.sun.xml.ws:jaxws-rt:2.3.1' implementation 'org.glassfish.metro:webservices-rt:2.4.4' implementation fileTree(dir: 'libs', include: ['*.jar']) + + // excel + implementation 'org.apache.poi:poi:5.2.3' // .xls + implementation 'org.apache.poi:poi-ooxml:5.2.3' // .xlsx } tasks.named('test') { diff --git a/src/main/java/com/jangburich/domain/store/controller/StoreController.java b/src/main/java/com/jangburich/domain/store/controller/StoreController.java index cc145d1..c7c061f 100644 --- a/src/main/java/com/jangburich/domain/store/controller/StoreController.java +++ b/src/main/java/com/jangburich/domain/store/controller/StoreController.java @@ -1,11 +1,16 @@ package com.jangburich.domain.store.controller; -import com.jangburich.domain.store.dto.response.StoreSearchDetailsResponse; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; import java.util.List; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; @@ -27,6 +32,7 @@ import com.jangburich.domain.store.dto.response.OrdersTodayResponse; import com.jangburich.domain.store.dto.response.PaymentGroupDetailResponse; import com.jangburich.domain.store.dto.response.SearchStoresResponse; +import com.jangburich.domain.store.dto.response.StoreSearchDetailsResponse; import com.jangburich.domain.store.dto.response.StoreTeamResponse; import com.jangburich.domain.store.service.StoreService; import com.jangburich.global.payload.Message; @@ -44,108 +50,130 @@ @RequestMapping("/store") public class StoreController { - private final StoreService storeService; - - @Operation(summary = "카테고리 별 가게 목록 조회", description = "카테고리 별로 가게 목록을 조회합니다.") - @PostMapping("/category") - public ResponseCustom> searchByCategory( - Authentication authentication, - @RequestParam(required = false, defaultValue = "3") Integer searchRadius, - @RequestParam(required = false, defaultValue = "ALL") Category category, - Double lat, - Double lon, Pageable pageable) { - return ResponseCustom.OK( - storeService.searchByCategory(AuthenticationParser.parseUserId(authentication), searchRadius, category, lat, lon, - pageable)); - } - - @Operation(summary = "매장 찾기(검색)", description = "검색어와 매장 유형에 맞는 매장을 검색합니다.") - @GetMapping("/search") - public ResponseCustom> searchStores( - Authentication authentication, - @RequestParam(required = false, defaultValue = "") String keyword, Pageable pageable) { - return ResponseCustom.OK( - storeService.searchStores(AuthenticationParser.parseUserId(authentication), keyword, pageable)); - } - - @Operation(summary = "매장 상세 페이지 조회", description = "매장을 상세 조회합니다.") - @GetMapping("/{storeId}") - public ResponseCustom storeSearchDetails( - Authentication authentication, - @PathVariable Long storeId - ) { - return ResponseCustom.OK(storeService.storeSearchDetails(AuthenticationParser.parseUserId(authentication), storeId)); - } - - @Operation(summary = "가게 등록", description = "신규 파트너 가게를 등록합니다.") - @PostMapping(value = "/create", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) - public ResponseCustom createStore( - Authentication authentication, - @Parameter(name = "image", description = "업로드 사진 데이터") @RequestPart(value = "image") MultipartFile image, - @RequestPart(value = "store") StoreCreateRequestDTO storeCreateRequestDTO, - @RequestPart(value = "menuImages", required = false) List menuImages) { - - storeService.createStore(AuthenticationParser.parseUserId(authentication), storeCreateRequestDTO, image, - menuImages); - return ResponseCustom.OK(Message.builder().message("success").build()); - } - - - @Operation(summary = "가게 정보 수정", description = "가게 정보를 수정합니다.") - @PatchMapping("/update") - public ResponseCustom updateStore(Authentication authentication, - @RequestBody StoreUpdateRequestDTO storeUpdateRequestDTO) { - storeService.updateStore(AuthenticationParser.parseUserId(authentication), storeUpdateRequestDTO); - return ResponseCustom.OK(Message.builder().message("success").build()); - } - - @Operation(summary = "가게 정보 조회", description = "가게 상세 정보를 조회합니다.") - @GetMapping("") - public ResponseCustom getStoreInfo(Authentication authentication) { - return ResponseCustom.OK(storeService.getStoreInfo(AuthenticationParser.parseUserId(authentication))); - } - - @Operation(summary = "결제 그룹 조회", description = "장부 결제 그룹을 조회합니다.") - @GetMapping("/payment_group") - public ResponseCustom> getPaymentGroup(Authentication authentication) { - return ResponseCustom.OK( - storeService.getPaymentGroup(AuthenticationParser.parseUserId(authentication))); - } - - @Operation(summary = "결제 그룹 상세 조회", description = "장부 결제 그룹을 상세 조회합니다.") - @GetMapping("/payment_group/{teamId}") - public ResponseCustom getPaymentGroupDetail(Authentication authentication, - @PathVariable Long teamId) { - return ResponseCustom.OK( - storeService.getPaymentGroupDetail(AuthenticationParser.parseUserId(authentication), teamId)); - } - - @Operation(summary = "결제 내역 조회", description = "가게에서 일어난 결제 내역을 조회합니다.") - @GetMapping("/payment_history") - public ResponseCustom getPaymentHistory(Authentication authentication) { - return ResponseCustom.OK( - storeService.getPaymentHistory(AuthenticationParser.parseUserId(authentication))); - } - - @Operation(summary = "지난 주문 조회", description = "가게에 있는 지난 주문을 조회합니다") - @GetMapping("/orders/last") - public ResponseCustom> getLastOrders(Authentication authentication) { - List ordersLast = storeService.getOrdersLast( - AuthenticationParser.parseUserId(authentication)); - return ResponseCustom.OK(ordersLast); - } - - @Operation(summary = "오늘 주문 조회", description = "가게에 있는 오늘 주문을 조회합니다") - @GetMapping("/orders/today") - public ResponseCustom getTodayOrders(Authentication authentication) { - return ResponseCustom.OK(storeService.getTodayOrders( - AuthenticationParser.parseUserId(authentication))); - } - - @Operation(summary = "주문 상세 조회", description = "가게에 있는 주문을 상세 조회합니다") - @GetMapping("/orders/{ordersId}") - public ResponseCustom getOrders(Authentication authentication, @RequestParam Long orderId) { - return ResponseCustom.OK( - storeService.getOrderDetails(AuthenticationParser.parseUserId(authentication), orderId)); - } + private final StoreService storeService; + + @Operation(summary = "카테고리 별 가게 목록 조회", description = "카테고리 별로 가게 목록을 조회합니다.") + @PostMapping("/category") + public ResponseCustom> searchByCategory( + Authentication authentication, + @RequestParam(required = false, defaultValue = "3") Integer searchRadius, + @RequestParam(required = false, defaultValue = "ALL") Category category, + Double lat, + Double lon, Pageable pageable) { + return ResponseCustom.OK( + storeService.searchByCategory(AuthenticationParser.parseUserId(authentication), searchRadius, category, lat, + lon, + pageable)); + } + + @Operation(summary = "매장 찾기(검색)", description = "검색어와 매장 유형에 맞는 매장을 검색합니다.") + @GetMapping("/search") + public ResponseCustom> searchStores( + Authentication authentication, + @RequestParam(required = false, defaultValue = "") String keyword, Pageable pageable) { + return ResponseCustom.OK( + storeService.searchStores(AuthenticationParser.parseUserId(authentication), keyword, pageable)); + } + + @Operation(summary = "매장 상세 페이지 조회", description = "매장을 상세 조회합니다.") + @GetMapping("/{storeId}") + public ResponseCustom storeSearchDetails( + Authentication authentication, + @PathVariable Long storeId + ) { + return ResponseCustom.OK( + storeService.storeSearchDetails(AuthenticationParser.parseUserId(authentication), storeId)); + } + + @Operation(summary = "가게 등록", description = "신규 파트너 가게를 등록합니다.") + @PostMapping(value = "/create", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseCustom createStore( + Authentication authentication, + @Parameter(name = "image", description = "업로드 사진 데이터") @RequestPart(value = "image") MultipartFile image, + @RequestPart(value = "store") StoreCreateRequestDTO storeCreateRequestDTO, + @RequestPart(value = "menuImages", required = false) List menuImages) { + + storeService.createStore(AuthenticationParser.parseUserId(authentication), storeCreateRequestDTO, image, + menuImages); + return ResponseCustom.OK(Message.builder().message("success").build()); + } + + @Operation(summary = "가게 정보 수정", description = "가게 정보를 수정합니다.") + @PatchMapping("/update") + public ResponseCustom updateStore(Authentication authentication, + @RequestBody StoreUpdateRequestDTO storeUpdateRequestDTO) { + storeService.updateStore(AuthenticationParser.parseUserId(authentication), storeUpdateRequestDTO); + return ResponseCustom.OK(Message.builder().message("success").build()); + } + + @Operation(summary = "가게 정보 조회", description = "가게 상세 정보를 조회합니다.") + @GetMapping("") + public ResponseCustom getStoreInfo(Authentication authentication) { + return ResponseCustom.OK(storeService.getStoreInfo(AuthenticationParser.parseUserId(authentication))); + } + + @Operation(summary = "결제 그룹 조회", description = "장부 결제 그룹을 조회합니다.") + @GetMapping("/payment_group") + public ResponseCustom> getPaymentGroup(Authentication authentication) { + return ResponseCustom.OK( + storeService.getPaymentGroup(AuthenticationParser.parseUserId(authentication))); + } + + @Operation(summary = "결제 그룹 상세 조회", description = "장부 결제 그룹을 상세 조회합니다.") + @GetMapping("/payment_group/{teamId}") + public ResponseCustom getPaymentGroupDetail(Authentication authentication, + @PathVariable Long teamId) { + return ResponseCustom.OK( + storeService.getPaymentGroupDetail(AuthenticationParser.parseUserId(authentication), teamId)); + } + + @Operation(summary = "결제 내역 조회", description = "가게에서 일어난 결제 내역을 조회합니다.") + @GetMapping("/payment_history") + public ResponseCustom getPaymentHistory(Authentication authentication) { + return ResponseCustom.OK( + storeService.getPaymentHistory(AuthenticationParser.parseUserId(authentication))); + } + + @Operation(summary = "지난 주문 조회", description = "가게에 있는 지난 주문을 조회합니다") + @GetMapping("/orders/last") + public ResponseCustom> getLastOrders(Authentication authentication) { + List ordersLast = storeService.getOrdersLast( + AuthenticationParser.parseUserId(authentication)); + return ResponseCustom.OK(ordersLast); + } + + @Operation(summary = "오늘 주문 조회", description = "가게에 있는 오늘 주문을 조회합니다") + @GetMapping("/orders/today") + public ResponseCustom getTodayOrders(Authentication authentication) { + return ResponseCustom.OK(storeService.getTodayOrders( + AuthenticationParser.parseUserId(authentication))); + } + + @Operation(summary = "주문 상세 조회", description = "가게에 있는 주문을 상세 조회합니다") + @GetMapping("/orders/{ordersId}") + public ResponseCustom getOrders(Authentication authentication, @RequestParam Long orderId) { + return ResponseCustom.OK( + storeService.getOrderDetails(AuthenticationParser.parseUserId(authentication), orderId)); + } + + @Operation(summary = "가게 엑셀 다운로드", description = "가게 장부 세부 내역을 엑셀로 제공합니다.") + @GetMapping("/excel") + public ResponseEntity getExcel( + // Authentication authentication, + @RequestParam(defaultValue = "1") Integer period + ) { + byte[] excel = storeService.createExcel("test-owner", period); + + String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + String fileName = "장부_세부내역_" + period + "개월_" + today + ".xlsx"; + String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8) + .replace("+", "%20"); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.set("Content-Disposition", + "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName); + + return ResponseEntity.ok().headers(headers).body(excel); + } } \ No newline at end of file diff --git a/src/main/java/com/jangburich/domain/store/domain/StoreChargeHistoryResponse.java b/src/main/java/com/jangburich/domain/store/domain/StoreChargeHistoryResponse.java index 083de70..1a0b49a 100644 --- a/src/main/java/com/jangburich/domain/store/domain/StoreChargeHistoryResponse.java +++ b/src/main/java/com/jangburich/domain/store/domain/StoreChargeHistoryResponse.java @@ -1,21 +1,26 @@ package com.jangburich.domain.store.domain; -import java.time.LocalDate; import java.time.LocalDateTime; +import com.jangburich.domain.point.domain.TransactionType; import com.querydsl.core.annotations.QueryProjection; public record StoreChargeHistoryResponse( Long id, LocalDateTime createdAt, String teamName, - Integer transactionedPoint + Long teamId, + Integer transactionedPoint, + TransactionType transactionType ) { @QueryProjection - public StoreChargeHistoryResponse(Long id, LocalDateTime createdAt, String teamName, Integer transactionedPoint) { + public StoreChargeHistoryResponse(Long id, LocalDateTime createdAt, String teamName, Long teamId, + Integer transactionedPoint, TransactionType transactionType) { this.id = id; this.createdAt = createdAt; this.teamName = teamName; + this.teamId = teamId; this.transactionedPoint = transactionedPoint; + this.transactionType = transactionType; } } diff --git a/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java b/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java index e5d8f48..7055062 100644 --- a/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java +++ b/src/main/java/com/jangburich/domain/store/domain/StoreTeam.java @@ -48,6 +48,9 @@ public class StoreTeam extends BaseEntity { @Column(name = "prepaid_expiration_date") private LocalDate prepaidExpirationDate; + @Column(name = "prepay_count") + private Integer prepayCount; + public void updatePersonalAllocatedPoint(Integer point) { this.personalAllocatedPoint = point; } diff --git a/src/main/java/com/jangburich/domain/store/domain/StoreTeamResponseDTO.java b/src/main/java/com/jangburich/domain/store/domain/StoreTeamResponseDTO.java index 608908d..28ebf77 100644 --- a/src/main/java/com/jangburich/domain/store/domain/StoreTeamResponseDTO.java +++ b/src/main/java/com/jangburich/domain/store/domain/StoreTeamResponseDTO.java @@ -10,6 +10,7 @@ public record StoreTeamResponseDTO( Long id, Integer remainPoint, + Integer point, Long teamId, String teamName, String teamDescription, @@ -18,10 +19,11 @@ public record StoreTeamResponseDTO( ) { @QueryProjection - public StoreTeamResponseDTO(Long id, Integer remainPoint, Long teamId, String teamName, String teamDescription, - Long storeId, LocalDateTime updatedAt) { + public StoreTeamResponseDTO(Long id, Integer remainPoint, Integer point, Long teamId, String teamName, + String teamDescription, Long storeId, LocalDateTime updatedAt) { this.id = id; this.remainPoint = remainPoint; + this.point = point; this.teamId = teamId; this.teamName = teamName; this.teamDescription = teamDescription; diff --git a/src/main/java/com/jangburich/domain/store/service/PrepayService.java b/src/main/java/com/jangburich/domain/store/service/PrepayService.java index 5b4fc8f..3941d8c 100644 --- a/src/main/java/com/jangburich/domain/store/service/PrepayService.java +++ b/src/main/java/com/jangburich/domain/store/service/PrepayService.java @@ -69,6 +69,7 @@ public Message prepay(String userId, PrepayRequest prepayRequest) { .transactionedPoint(prepayRequest.prepayAmount()) .user(user) .store(store) + .team(team) .build(); pointTransactionRepository.save(pointTransaction); @@ -94,6 +95,7 @@ public Message prepay(String userId, PrepayRequest prepayRequest) { StoreTeam storeTeam = storeAndTeam.get(); storeTeam.setPersonalAllocatedPoint(prepayRequest.personalAllocatedAmount()); storeTeam.recharge(prepayRequest.prepayAmount()); + storeTeam.setPrepayCount(storeTeam.getPrepayCount() + 1); return Message.builder() .message("매장 선결제가 완료되었습니다.") diff --git a/src/main/java/com/jangburich/domain/store/service/StoreService.java b/src/main/java/com/jangburich/domain/store/service/StoreService.java index cec9028..bdeab5b 100644 --- a/src/main/java/com/jangburich/domain/store/service/StoreService.java +++ b/src/main/java/com/jangburich/domain/store/service/StoreService.java @@ -1,11 +1,31 @@ package com.jangburich.domain.store.service; +import java.awt.*; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.time.DayOfWeek; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; - +import java.util.Objects; + +import org.apache.poi.ss.usermodel.BorderStyle; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.CellStyle; +import org.apache.poi.ss.usermodel.FillPatternType; +import org.apache.poi.ss.usermodel.Font; +import org.apache.poi.ss.usermodel.HorizontalAlignment; +import org.apache.poi.ss.usermodel.IndexedColors; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.apache.poi.ss.usermodel.VerticalAlignment; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFCellStyle; +import org.apache.poi.xssf.usermodel.XSSFColor; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; @@ -25,6 +45,7 @@ import com.jangburich.domain.owner.domain.Owner; import com.jangburich.domain.owner.domain.repository.OwnerRepository; import com.jangburich.domain.payment.domain.repository.TeamChargeHistoryRepository; +import com.jangburich.domain.point.domain.TransactionType; import com.jangburich.domain.point.domain.repository.PointTransactionRepository; import com.jangburich.domain.store.domain.Category; import com.jangburich.domain.store.domain.Store; @@ -33,14 +54,15 @@ import com.jangburich.domain.store.domain.StoreCreateRequestDTO; import com.jangburich.domain.store.domain.StoreGetResponseDTO; import com.jangburich.domain.store.domain.StoreTeam; +import com.jangburich.domain.store.domain.StoreTeamResponseDTO; import com.jangburich.domain.store.domain.StoreUpdateRequestDTO; -import com.jangburich.domain.store.dto.response.StoreTeamResponse; import com.jangburich.domain.store.dto.response.OrdersDetailResponse; import com.jangburich.domain.store.dto.response.OrdersGetResponse; import com.jangburich.domain.store.dto.response.OrdersTodayResponse; import com.jangburich.domain.store.dto.response.PaymentGroupDetailResponse; import com.jangburich.domain.store.dto.response.SearchStoresResponse; import com.jangburich.domain.store.dto.response.StoreSearchDetailsResponse; +import com.jangburich.domain.store.dto.response.StoreTeamResponse; import com.jangburich.domain.store.exception.OrdersNotFoundException; import com.jangburich.domain.store.repository.StoreRepository; import com.jangburich.domain.store.repository.StoreTeamRepository; @@ -82,7 +104,8 @@ public void createStore(String authentication, StoreCreateRequestDTO storeCreate Owner owner = ownerRepository.findByUser(user) .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); - List dayOfWeekList = DayOfWeekConverter.convertStringToDayOfWeekList(storeCreateRequestDTO.getDayOfWeek()); + List dayOfWeekList = DayOfWeekConverter.convertStringToDayOfWeekList( + storeCreateRequestDTO.getDayOfWeek()); Store store = storeRepository.save(Store.of(owner, storeCreateRequestDTO, dayOfWeekList)); @@ -416,4 +439,212 @@ public StoreSearchDetailsResponse storeSearchDetails(String userId, Long storeId return storeSearchDetails; } + + public byte[] createExcel(String userId, Integer period) { + User user = userRepository.findByProviderId(userId) + .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); + + Owner owner = ownerRepository.findByUser(user) + .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); + + Store store = storeRepository.findByOwner(owner) + .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_AUTHENTICATION)); + + Workbook workbook = new XSSFWorkbook(); + Sheet sheet = workbook.createSheet(LocalDate.now().minusMonths(period) + "~" + LocalDate.now()); + + CellStyle rightCellStyle = sheet.getWorkbook().createCellStyle(); + rightCellStyle.setWrapText(true); + rightCellStyle.setAlignment(HorizontalAlignment.RIGHT); + + // 헤더 행 생성 + Row headerRow = sheet.createRow(0); + + // 헤더 스타일 생성 + XSSFCellStyle headerStyle = (XSSFCellStyle) sheet.getWorkbook().createCellStyle(); + + // 헥사 색상 설정 + XSSFColor customColor = new XSSFColor(Color.decode("#FF7048"), null); + + headerStyle.setFillForegroundColor(customColor); // 배경색 설정 + headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); // 패턴 설정 + headerStyle.setAlignment(HorizontalAlignment.CENTER); // 가로 가운데 정렬 + headerStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 세로 가운데 정렬 + + headerStyle.setBorderTop(BorderStyle.THIN); // 위쪽 테두리 + headerStyle.setBorderBottom(BorderStyle.THIN); // 아래쪽 테두리 + headerStyle.setBorderLeft(BorderStyle.THIN); // 왼쪽 테두리 + headerStyle.setBorderRight(BorderStyle.THIN); // 오른쪽 테두리 + headerStyle.setTopBorderColor(IndexedColors.BLACK.getIndex()); // 위쪽 테두리 색상 + headerStyle.setBottomBorderColor(IndexedColors.BLACK.getIndex()); // 아래쪽 테두리 색상 + headerStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex()); // 왼쪽 테두리 색상 + headerStyle.setRightBorderColor(IndexedColors.BLACK.getIndex()); // 오른쪽 테두리 색상 + + // 헤더 글꼴 스타일 설정 + Font headerFont = sheet.getWorkbook().createFont(); + headerFont.setBold(true); // 볼드체 + headerFont.setColor(IndexedColors.WHITE.getIndex()); // 글자 색상 + headerStyle.setFont(headerFont); + + // 헤더 내용 배열로 관리 + String[] headers = { + "① 선결제일자", + "② 그룹명", + "③ 선결제액", + "④ 부가세 (③ * 1/11)", + "⑤ 공급가액 (③-④)", + "⑥ 누적선결제건수", + "⑦ 누적선결제액", + "⑧ 누적차감건수", + "⑨ 누적차감금액", + "⑩ 잔여선결제액 (⑦-⑨)" + }; + + // 헤더 작성 + for (int i = 0; i < headers.length; i++) { + Cell cell = headerRow.createCell(i + 1); // 셀 생성 (i + 1로 시작) + cell.setCellValue(headers[i]); // 헤더 텍스트 설정 + cell.setCellStyle(headerStyle); // 스타일 적용 + } + + // 행 높이 설정 (헤더 텍스트가 잘 보이도록) + headerRow.setHeightInPoints(20); // 원하는 높이로 설정 + + List prepayHistoryResponses = pointTransactionRepository.findAllByStore(store) + .stream() + .filter(storeChargeHistoryResponse -> + storeChargeHistoryResponse.transactionType() == TransactionType.PREPAY + && storeChargeHistoryResponse.createdAt().isAfter(LocalDateTime.now().minusMonths(period)) + ) + .sorted(Comparator.comparing(StoreChargeHistoryResponse::createdAt)) // 오름차순 정렬 + .toList(); + + List foodPurchaseHistoryResponses = pointTransactionRepository.findAllByStore(store) + .stream() + .filter( + storeChargeHistoryResponse -> storeChargeHistoryResponse.transactionType() + == TransactionType.FOOD_PURCHASE) + .toList(); + + Integer totalPrePay = 0; + + int rowIndex = 1; + for (StoreChargeHistoryResponse prepayHistoryResponse : prepayHistoryResponses) { + Row dataRow = sheet.createRow(rowIndex++); + dataRow.createCell(1) + .setCellValue(prepayHistoryResponse.createdAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + dataRow.createCell(2).setCellValue(prepayHistoryResponse.teamName()); + totalPrePay += prepayHistoryResponse.transactionedPoint(); + dataRow.createCell(3).setCellValue(prepayHistoryResponse.transactionedPoint()); + int surtax = prepayHistoryResponse.transactionedPoint() / 11; + dataRow.createCell(4).setCellValue(surtax); + dataRow.createCell(5).setCellValue(prepayHistoryResponse.transactionedPoint() - surtax); + StoreTeam storeTeam = storeTeamRepository.findByStoreIdAndTeamId(store.getId(), + prepayHistoryResponse.teamId()) + .orElseThrow(() -> new DefaultNullPointerException(ErrorCode.INVALID_CHECK)); + dataRow.createCell(6).setCellValue(storeTeam.getPrepayCount()); + dataRow.createCell(7).setCellValue(storeTeam.getPoint()); // 누적 선결제 금액 + dataRow.createCell(8) + .setCellValue(foodPurchaseHistoryResponses.stream().filter(storeChargeHistoryResponse -> Objects.equals( + storeChargeHistoryResponse.teamId(), prepayHistoryResponse.teamId())).count()); + dataRow.createCell(9).setCellValue(storeTeam.getPoint() - storeTeam.getRemainPoint()); + dataRow.createCell(10).setCellValue(storeTeam.getRemainPoint()); + } + rowIndex += 2; + + Row dataRow = sheet.createRow(rowIndex++); + + Cell cell = dataRow.createCell(3); + cell.setCellValue("기간"); + cell.setCellStyle(headerStyle); + + Cell periodCell = dataRow.createCell(4); + periodCell.setCellValue(LocalDate.now().minusMonths(period) + " ~\n " + LocalDate.now()); + periodCell.setCellStyle(rightCellStyle); + dataRow.setHeightInPoints((short)(dataRow.getHeightInPoints() * 2)); + + dataRow = sheet.createRow(rowIndex++); + cell = dataRow.createCell(3); + cell.setCellValue("① 총 선 결제액(매출)"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(totalPrePay); + periodCell.setCellStyle(rightCellStyle); + + dataRow = sheet.createRow(rowIndex++); + cell = dataRow.createCell(3); + cell.setCellValue("② 부가세 총액 (① * 1/11)"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(totalPrePay / 11); + periodCell.setCellStyle(rightCellStyle); + + dataRow = sheet.createRow(rowIndex++); + cell = dataRow.createCell(3); + cell.setCellValue("③ 총 공급가액 (①-②)"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(totalPrePay - totalPrePay / 11); + periodCell.setCellStyle(rightCellStyle); + + int totalPoint = storeTeamRepository.findAllByStore(store) + .stream() + .mapToInt(StoreTeamResponseDTO::point) + .sum(); + + int remainPoint = storeTeamRepository.findAllByStore(store) + .stream() + .mapToInt(StoreTeamResponseDTO::remainPoint) + .sum(); + + dataRow = sheet.createRow(rowIndex++); + cell = dataRow.createCell(3); + cell.setCellValue("④ 총 누적 선 결제액"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(totalPoint); + periodCell.setCellStyle(rightCellStyle); + + dataRow = sheet.createRow(rowIndex++); + cell = dataRow.createCell(3); + cell.setCellValue("⑤ 총 누적 차감액"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(totalPoint - remainPoint); + periodCell.setCellStyle(rightCellStyle); + + dataRow = sheet.createRow(rowIndex); + cell = dataRow.createCell(3); + cell.setCellValue("⑥ 총 잔여 선 결제액 (④-⑤)"); + cell.setCellStyle(headerStyle); + + periodCell = dataRow.createCell(4); + periodCell.setCellValue(remainPoint); + periodCell.setCellStyle(rightCellStyle); + + for (int i = 0; i < headerRow.getLastCellNum(); i++) { + sheet.autoSizeColumn(i); + int currentWidth = sheet.getColumnWidth(i); + sheet.setColumnWidth(i, (int)(currentWidth * 1.2)); + } + + // 엑셀 파일을 바이트 배열로 변환 + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + workbook.write(bos); + return bos.toByteArray(); + } catch (IOException e) { + throw new RuntimeException("엑셀 파일 생성 중 오류가 발생했습니다.", e); + } finally { + try { + workbook.close(); // close 중 발생한 예외 처리 + } catch (IOException e) { + System.err.println("Workbook 닫는 중 오류 발생: " + e.getMessage()); + } + } + } } \ No newline at end of file