diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b60da544c7daa73d71edd30e38824c95e80401b..72083a6a4f02ab795450a19d01b8894fce444432 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,131 @@ +## [1.0.60-RELEASE] +### Added +- calculate Theoretical Amount + +### Changed +- Changes in existing function. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +## [1.0.59-RELEASE] +### Added +- Add new conditions in apply promo code to cart. + +### Changed +- Changes in existing function. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +### Fixed +- Any bug fixes. +## [1.0.58-RELEASE] +### Added +- Rate of adding products to the cart. + +### Changed +- Changes in isItemIncludedOrExcluded function. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +## [1.0.57-RELEASE] +### Added +- calculate THEORETICAL_TURNOVER. + +### Changed +- Changes in isItemIncludedOrExcluded function. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +## [1.0.56-RELEASE] +### Added +- Add new conditions in apply promo code to cart. + +### Changed +- Changes in isItemIncludedOrExcluded function. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +### Fixed +- Any bug fixes. +## [1.0.54-RELEASE] +### Added +- Add fetching gift card and voucher by code. + +### Changed +- Changes in dto, mapper, +- Changes in reload method to calculate total price and apply all discount of Promo Code, Gift Card and Voucher. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +### Fixed +- Any bug fixes. +## [1.0.54-RELEASE] +### Added +- Add features to add, remove and apply promo code to cart. +- Add method to remove invalid promo codes from cart. +- Add method to execute all existing methods of Gift Card and Voucher. + +### Changed +- Changes in dto, mapper, +- Changes in reload method to calculate total price and apply all discount of Promo Code, Gift Card and Voucher. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +### Fixed +- Any bug fixes. +## [1.0.55-RELEASE] +### Added +- Add two endpoints to apply and remove gift card code + +### Changed +- apply and remove Voucher from cart + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +## [1.0.55-RELEASE] +### Added +- add gift card to cart delete and duplicate gift card in cart + +### Changed +- Changes in existing functionality. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. ## [1.0.54-RELEASE] ### Added - MYD-619/some changes @@ -1029,4 +1157,4 @@ - Any bug fixes. ### Security -- Any security improvements. +- Any security improvements. \ No newline at end of file diff --git a/pom.xml b/pom.xml index 5092315c31c027c60d35f39d851c173b42099e62..6ce7e9f55dbc7de711b944c5ad83f56f22bdabb8 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ <dependency> <groupId>com.marketingconfort</groupId> <artifactId>mydressin-common</artifactId> - <version>1.0.119-RELEASE</version> + <version>1.0.145-RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> @@ -50,7 +50,6 @@ <artifactId>modelmapper</artifactId> <version>2.4.4</version> </dependency> - </dependencies> <build> <plugins> diff --git a/src/main/java/com/marketingconfort/mydressin/constants/Paths.java b/src/main/java/com/marketingconfort/mydressin/constants/Paths.java index 24f318cb853a74985abff4c2ea09b45e5c06f773..8f62f182feb3ce0099f7ff10dfd2debe3be8b265 100644 --- a/src/main/java/com/marketingconfort/mydressin/constants/Paths.java +++ b/src/main/java/com/marketingconfort/mydressin/constants/Paths.java @@ -5,9 +5,24 @@ public class Paths { public static final String API_CART_CONFIG = "/api/cart/cart-count-config"; public static final String GET_BY_ID= "/{id}"; - - public static final String CHECK_STOCK_CART_PRODUCTS= "/check-stock-cart-products/{clientId}"; public static final String DECREMENT_STOCK_CART_PRODUCTS= "/decrement-stock-cart-products/{clientId}"; + public static final String APPLY_GIFT_CARD= "/apply-giftcard"; + public static final String REMOVE_GIFT_CARD= "/remove-giftcard"; + public static final String APPLY_VOUCHER= "/apply-voucher"; + public static final String REMOVE_VOUCHER= "/remove-voucher"; + + + public static final String API_CART_PROMO_CODE = "/api/cart/promoCode"; + public static final String CHECK_APPLY_PROMO_CODE = "/check-apply-promoCode"; + public static final String REMOVE_PROMO_CODE = "/delete-promoCode"; + + //////// Statistic paths + + public static final String STATISTICS_API ="/api/cart/statistics"; + public static final String THEORETICAL_TURNOVER = "/theoretical-revenue"; + public static final String ADD_TO_CART_TRENDS = "/add-to-cart-trends"; + public static final String CART_COUNT = "/cart-count"; + public static final String THEORETICAL_AMOUNT = "/theoretical-amount"; } diff --git a/src/main/java/com/marketingconfort/mydressin/controllers/CartController.java b/src/main/java/com/marketingconfort/mydressin/controllers/CartController.java index 947b777dd8956f99a6a0da5546a2fc061be3616e..c07885df22e9d11cc1c9fd789771f8c1b52a92cc 100644 --- a/src/main/java/com/marketingconfort/mydressin/controllers/CartController.java +++ b/src/main/java/com/marketingconfort/mydressin/controllers/CartController.java @@ -15,6 +15,8 @@ import com.marketingconfort.mydressin.mappers.CartMapper; import com.marketingconfort.mydressin.services.CartService; import com.marketingconfort.mydressin.services.ClientService; import com.marketingconfort.mydressin.services.ProductStockService; +import com.marketingconfort.starter.core.exceptions.FunctionalException; +import com.marketingconfort.starter.core.exceptions.TechnicalException; import lombok.AllArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -65,6 +67,8 @@ public class CartController { return ResponseEntity.ok(cartDTO); } catch (CartNotFoundException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // HTTP 404 si le panier n'est pas trouvé + } catch (TechnicalException e) { + throw new RuntimeException(e); } } @GetMapping("/{clientId}") @@ -72,7 +76,7 @@ public class CartController { try{ CartDTO cartDTO = cartService.getCartByClientId(clientId); return ResponseEntity.ok(cartDTO); - }catch (CartNotFoundException e){ + }catch (CartNotFoundException | TechnicalException e){ return ResponseEntity.status(HttpStatus.NOT_FOUND).body( e.getMessage()); } @@ -105,12 +109,12 @@ public class CartController { } @PostMapping("/add-products-from-localStorage") public ResponseEntity<CartDTO> addProductsFromLocalStorage(@RequestBody - LocalStorageRequest request) { + LocalStorageRequest request) throws TechnicalException { CartDTO updatedCart = cartService.addProductsToCartFromLocalStorage(request.getId(), request.getCart()); return ResponseEntity.ok(updatedCart); } @PostMapping("/reload") - public ResponseEntity<CartDTO> reloadCart(@RequestBody LocalStorageRequest request) { + public ResponseEntity<CartDTO> reloadCart(@RequestBody LocalStorageRequest request) throws TechnicalException { CartDTO updatedCart = cartService.reload(request.getCart()); return ResponseEntity.ok(updatedCart); @@ -136,4 +140,37 @@ public class CartController { return cartService.decrementStockForAllCartProducts(clientId); } + @PostMapping(Paths.APPLY_GIFT_CARD) + public ResponseEntity<CartDTO> applyGiftcard(@RequestParam Long clientId, @RequestParam String giftcardCode) { + try { + CartDTO cartDTO = cartService.applyGiftcardToCart(clientId, giftcardCode); + return ResponseEntity.ok(cartDTO); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + } + + @PostMapping(Paths.REMOVE_GIFT_CARD) + public ResponseEntity<CartDTO> removeGiftcard(@RequestParam Long clientId) throws TechnicalException { + CartDTO cartDTO = cartService.removeGiftcardFromCart(clientId); + return ResponseEntity.ok(cartDTO); + } + + @PostMapping(Paths.APPLY_VOUCHER) + public ResponseEntity<CartDTO> applyVoucher(@RequestParam Long clientId, @RequestParam String voucherCode) { + try { + CartDTO cartDTO = cartService.applyVoucherToCart(clientId, voucherCode); + return ResponseEntity.ok(cartDTO); + } catch (Exception e) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null); + } + } + + @PostMapping(Paths.REMOVE_VOUCHER) + public ResponseEntity<CartDTO> removeVoucher(@RequestParam Long clientId) throws TechnicalException { + CartDTO cartDTO = cartService.removeVoucherFromCart(clientId); + return ResponseEntity.ok(cartDTO); + } + + } diff --git a/src/main/java/com/marketingconfort/mydressin/controllers/ItemCartController.java b/src/main/java/com/marketingconfort/mydressin/controllers/ItemCartController.java index 160bcf16f1c333fecb355e6d6de2f1d55ef39dac..5f8ed9c41529834c0cfbcecb7e747c14357e4902 100644 --- a/src/main/java/com/marketingconfort/mydressin/controllers/ItemCartController.java +++ b/src/main/java/com/marketingconfort/mydressin/controllers/ItemCartController.java @@ -5,6 +5,7 @@ import com.marketingconfort.mydressin.common.cart.models.ItemCart; import com.marketingconfort.mydressin.dtos.*; import com.marketingconfort.mydressin.repositories.ItemCartRepository; import com.marketingconfort.mydressin.services.ItemCartService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; import lombok.AllArgsConstructor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -24,7 +25,7 @@ public class ItemCartController { private ItemCartRepository itemCartRepository; @PostMapping("/add-product") - public ResponseEntity<CartDTO> addProductToCartByClientId(@RequestBody ItemRequestDTO itemRequest) { + public ResponseEntity<CartDTO> addProductToCartByClientId(@RequestBody ItemRequestDTO itemRequest) throws TechnicalException { System.out.println(itemRequest.getProductId()); CartDTO cartDTO = itemCartService.addProductToCartByClientId(itemRequest); return ResponseEntity.ok(cartDTO); @@ -32,7 +33,7 @@ public class ItemCartController { } @PutMapping("/update-item") - public ResponseEntity<CartDTO> updateItemCartById(@RequestParam Long itemCartId, @RequestParam Long quantity) { + public ResponseEntity<CartDTO> updateItemCartById(@RequestParam Long itemCartId, @RequestParam Long quantity) throws TechnicalException { CartDTO cartDTO = itemCartService.updateItemCartById(itemCartId, quantity); @@ -40,19 +41,19 @@ public class ItemCartController { } @DeleteMapping("/delete-item") - public ResponseEntity<CartDTO> deleteItemCartById(@RequestParam Long clientId, @RequestParam Long itemCartId) { + public ResponseEntity<CartDTO> deleteItemCartById(@RequestParam Long clientId, @RequestParam Long itemCartId) throws TechnicalException { CartDTO cartDTO = itemCartService.deleteItemCartById(clientId, itemCartId); return ResponseEntity.ok(cartDTO); } @DeleteMapping("/delete-itemBO") - public ResponseEntity<CartDTO> deleteItemCartByIdFromBO(@RequestParam Long clientId, @RequestParam Long itemCartId) { + public ResponseEntity<CartDTO> deleteItemCartByIdFromBO(@RequestParam Long clientId, @RequestParam Long itemCartId) throws TechnicalException { CartDTO cartDTO = itemCartService.deleteItemCartByIdFromBO(clientId, itemCartId); return ResponseEntity.ok(cartDTO); } @PostMapping("/add-product-localStorage") - public ResponseEntity<CartDTO> addProductToCartFromLocalStorage(@RequestBody LocalStorageRequest request) { + public ResponseEntity<CartDTO> addProductToCartFromLocalStorage(@RequestBody LocalStorageRequest request) throws TechnicalException { if(request.getCart() == null || request.getId() == null || request.getQuantity() == null) { return ResponseEntity.badRequest().body(null); } @@ -63,12 +64,12 @@ public class ItemCartController { return ResponseEntity.ok(updatedCart); } @PutMapping("/update-item-localStorage") - public ResponseEntity<CartDTO> updateItemCartInCartFromLocalStorage(@RequestBody LocalStorageRequest request) { + public ResponseEntity<CartDTO> updateItemCartInCartFromLocalStorage(@RequestBody LocalStorageRequest request) throws TechnicalException { CartDTO updatedCart = itemCartService.updateItemCartById(request.getCart(), request.getId(), request.getQuantity()); return ResponseEntity.ok(updatedCart); } @DeleteMapping("/delete-item-localStorage") - public ResponseEntity<CartDTO> deleteItemCartInCartFromLocalStorage(@RequestBody LocalStorageRequest request) { + public ResponseEntity<CartDTO> deleteItemCartInCartFromLocalStorage(@RequestBody LocalStorageRequest request) throws TechnicalException { CartDTO updatedCart = itemCartService.deleteItemCartById(request.getCart(), request.getId()); return ResponseEntity.ok(updatedCart); } @@ -94,5 +95,4 @@ public class ItemCartController { .collect(Collectors.toList()); } - -} \ No newline at end of file +} diff --git a/src/main/java/com/marketingconfort/mydressin/controllers/CodePromoController.java b/src/main/java/com/marketingconfort/mydressin/controllers/PromoCodeController.java similarity index 65% rename from src/main/java/com/marketingconfort/mydressin/controllers/CodePromoController.java rename to src/main/java/com/marketingconfort/mydressin/controllers/PromoCodeController.java index 5e44b6385433b58f578f70fbdfd812a209ff3265..7a2a84a6a3b6f6866838da9911497b3aa09062d8 100644 --- a/src/main/java/com/marketingconfort/mydressin/controllers/CodePromoController.java +++ b/src/main/java/com/marketingconfort/mydressin/controllers/PromoCodeController.java @@ -1,26 +1,28 @@ package com.marketingconfort.mydressin.controllers; +import com.marketingconfort.mydressin.constants.Paths; import com.marketingconfort.mydressin.dtos.CartDTO; import com.marketingconfort.mydressin.exceptions.PromoCodeExpiredException; import com.marketingconfort.mydressin.exceptions.PromoCodeNotFoundException; -import com.marketingconfort.mydressin.services.CodePromoService; -import lombok.AllArgsConstructor; +import com.marketingconfort.mydressin.services.CartService; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @RestController -@RequestMapping("/api/cart/promoCode") -@AllArgsConstructor -public class CodePromoController { - private final CodePromoService CodePromoService; +@RequestMapping(Paths.API_CART_PROMO_CODE) +public class PromoCodeController { + private final CartService cartService; - @PostMapping("/checkPromoCode") - public ResponseEntity<?> checkPromoCode(@RequestParam String Code, @RequestParam Long ClientId) { + public PromoCodeController(CartService cartService) { + this.cartService = cartService; + } + + @PostMapping(Paths.CHECK_APPLY_PROMO_CODE) + public ResponseEntity<?> checkAndApplyPromoCode(@RequestParam String code, @RequestParam Long clientId) { try { - CartDTO cartDto = CodePromoService.checkCodePromoByClientId(Code, ClientId); - return ResponseEntity.ok(cartDto); + return ResponseEntity.ok(cartService.checkAndApplyPromoCodeToCart(code, clientId)); } catch (PromoCodeNotFoundException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage()); } catch (PromoCodeExpiredException e) { @@ -31,11 +33,10 @@ public class CodePromoController { } } - - @DeleteMapping("/delete") + @DeleteMapping(Paths.REMOVE_PROMO_CODE) public ResponseEntity<CartDTO> removePromoCodeFromCartByClientId(@RequestParam Long clientId, @RequestParam Long promoCodeId) { try { - CartDTO updatedCart = CodePromoService.removeCodePromoFromCart(clientId, promoCodeId); + CartDTO updatedCart = cartService.removeCodePromoFromCart(clientId, promoCodeId); return ResponseEntity.ok(updatedCart); diff --git a/src/main/java/com/marketingconfort/mydressin/controllers/SessionOrderController.java b/src/main/java/com/marketingconfort/mydressin/controllers/SessionOrderController.java index d36cca2e2429dc54b27c0c19890b9b1ecad99d91..a52b68ae4fcf8877c69e65136bd6c43fa2d90a65 100644 --- a/src/main/java/com/marketingconfort/mydressin/controllers/SessionOrderController.java +++ b/src/main/java/com/marketingconfort/mydressin/controllers/SessionOrderController.java @@ -54,8 +54,6 @@ public class SessionOrderController { } } - - @DeleteMapping("/delete-order/{orderId}") public ResponseEntity<Void> deleteOrderById(@PathVariable Long orderId) { boolean deleted = sessionOrderService.deleteOrderById(orderId); diff --git a/src/main/java/com/marketingconfort/mydressin/controllers/StatisticsController.java b/src/main/java/com/marketingconfort/mydressin/controllers/StatisticsController.java new file mode 100644 index 0000000000000000000000000000000000000000..0f8209120d1ed5f5434583fce2e575a73d33a4ad --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/controllers/StatisticsController.java @@ -0,0 +1,119 @@ +package com.marketingconfort.mydressin.controllers; +import com.marketingconfort.mydressin.constants.Paths; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.ChartDataDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.DailyAmountDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.TheoreticalAmountRequestDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.TheoreticalAmountResponseDTO; +import com.marketingconfort.mydressin.services.StatisticsService; +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import com.marketingconfort.starter.core.exceptions.FunctionalException; +import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.format.annotation.DateTimeFormat; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequestMapping(Paths.STATISTICS_API) +@AllArgsConstructor +public class StatisticsController { + + private final StatisticsService statisticsService; + private static final Logger logger = LoggerFactory.getLogger(StatisticsController.class); + + @GetMapping(Paths.THEORETICAL_TURNOVER) + public ResponseEntity<Double> getTheoreticalRevenue( + @RequestParam(required = false) List<String> sources, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) throws TechnicalException { + + logger.info("Received request with sources: {}, startDate: {}, endDate: {}", sources, startDate, endDate); + + List<ItemSource> itemSources = null; + if (sources != null) { + itemSources = sources.stream() + .map(sourceStr -> { + try { + return ItemSource.valueOf(sourceStr.toUpperCase()); + } catch (IllegalArgumentException e) { + logger.error("Invalid ItemSource value: {}", sourceStr); + try { + throw new FunctionalException("INVALID_ITEM_SOURCE"); + } catch (FunctionalException ex) { + throw new RuntimeException(ex); + } + } + }) + .collect(Collectors.toList()); + } + + double revenue = statisticsService.calculateTheoreticalRevenue(itemSources, startDate, endDate); + logger.info("Calculated theoretical revenue: {}", revenue); + return ResponseEntity.ok(revenue); + } + + @GetMapping(Paths.ADD_TO_CART_TRENDS) + public ResponseEntity<ChartDataDTO> getAddToCartTrends( + @RequestParam(required = false) List<String> sources) throws TechnicalException { + + List<ItemSource> itemSources = null; + if (sources != null && !sources.isEmpty()) { + itemSources = sources.stream() + .map(sourceStr -> { + try { + return ItemSource.valueOf(sourceStr.toUpperCase()); + } catch (IllegalArgumentException e) { + logger.error("Invalid ItemSource value: {}", sourceStr); + try { + throw new FunctionalException("INVALID_ITEM_SOURCE"); + } catch (FunctionalException ex) { + throw new RuntimeException(ex); + } + } + }) + .collect(Collectors.toList()); + } + + ChartDataDTO chartData = statisticsService.getAddToCartTrends(itemSources); + return ResponseEntity.ok(chartData); + } + + @GetMapping(Paths.CART_COUNT) + public ResponseEntity<Long> getTotalCarts( + @RequestParam(required = false) List<String> sources, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startDate, + @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endDate) { + + List<ItemSource> itemSources = null; + if (sources != null) { + itemSources = sources.stream() + .map(sourceStr -> { + try { + return ItemSource.valueOf(sourceStr.toUpperCase()); + } catch (IllegalArgumentException e) { + logger.error("Invalid ItemSource value: {}", sourceStr); + try { + throw new FunctionalException("INVALID_ITEM_SOURCE"); + } catch (FunctionalException ex) { + throw new RuntimeException(ex); + } + } + }) + .collect(Collectors.toList()); + } + Long totalCarts = statisticsService.getTotalCarts(itemSources, startDate, endDate); + return ResponseEntity.ok(totalCarts); + } + + @PostMapping(Paths.THEORETICAL_AMOUNT) + public ResponseEntity<TheoreticalAmountResponseDTO> getTheoreticalAmount(@RequestBody TheoreticalAmountRequestDTO requestDTO) { + TheoreticalAmountResponseDTO responseDTO = statisticsService.getTheoreticalAmountResponse(requestDTO); + return ResponseEntity.ok(responseDTO); + } + +} diff --git a/src/main/java/com/marketingconfort/mydressin/converters/StringToItemSourceConverter.java b/src/main/java/com/marketingconfort/mydressin/converters/StringToItemSourceConverter.java new file mode 100644 index 0000000000000000000000000000000000000000..c45c69e428419595e78d729a94ad3f01bd6cb7d6 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/converters/StringToItemSourceConverter.java @@ -0,0 +1,17 @@ +package com.marketingconfort.mydressin.converters; + +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class StringToItemSourceConverter implements Converter<String, ItemSource> { + + @Override + public ItemSource convert(String source) { + if (source == null || source.isEmpty()) { + return null; + } + return ItemSource.valueOf(source.toUpperCase()); + } +} \ No newline at end of file diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeRequest.java b/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..2be88531165887f26342a62e93c8332fd5e8d69a --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeRequest.java @@ -0,0 +1,13 @@ +package com.marketingconfort.mydressin.dtos; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +public class ApplyPromoCodeRequest { + private String promoCode; + private Long clientId; +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeResponse.java b/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..907177481e5667d60511ffb70a5e1109b58b3cb1 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/ApplyPromoCodeResponse.java @@ -0,0 +1,16 @@ +package com.marketingconfort.mydressin.dtos; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +@Builder +@AllArgsConstructor +public class ApplyPromoCodeResponse { + private boolean applied; + private String message; + private CartDTO updatedCart; +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/CartDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/CartDTO.java index 16ebdf8c4462d4bc5514072209b42bdeb969ba3a..ebd622a4b4a8df9f26c40de5afbb1525315744f4 100644 --- a/src/main/java/com/marketingconfort/mydressin/dtos/CartDTO.java +++ b/src/main/java/com/marketingconfort/mydressin/dtos/CartDTO.java @@ -1,9 +1,13 @@ package com.marketingconfort.mydressin.dtos; +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; +import com.marketingconfort.mydressin.common.cart.dtos.VoucherDTO; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -14,6 +18,8 @@ public class CartDTO { private Long id; private Long clientId; private double totalPrice; + private double subTotalPrice; + private double promoCodeDiscounts; private String typeCountDown; private List<String> errorsMessages = new ArrayList<>(); @@ -25,6 +31,13 @@ public class CartDTO { private List<Long> promoCodeIds = new ArrayList<>(); private List<Long> giftCardIds; private CountdownDTO countdown; + private GiftCardDTO appliedGiftCard; + private String appliedGiftCardCode; + private Long giftCardBalanceUsed; + + private VoucherDTO appliedVoucher; + private String appliedVoucherCode; + private Long voucherBalanceUsed; public CartDTO(Long clientId, String message) { diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/ItemCartDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/ItemCartDTO.java index 1abdf4072beedfa40dcf329ad6396cf199ef5876..0aeb72734c9345f9dd8a4c7ea2d3e896c56f7b27 100644 --- a/src/main/java/com/marketingconfort/mydressin/dtos/ItemCartDTO.java +++ b/src/main/java/com/marketingconfort/mydressin/dtos/ItemCartDTO.java @@ -1,6 +1,6 @@ package com.marketingconfort.mydressin.dtos; - +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductCartDTO; import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; @@ -9,8 +9,9 @@ import com.marketingconfort.mydressin.common.stockmanagement.enumurations.Produc import lombok.Getter; import lombok.Setter; +import java.math.BigDecimal; import java.time.LocalDateTime; - +import java.util.List; @Setter @Getter @@ -20,6 +21,8 @@ public class ItemCartDTO { private ProductType productType; private Long productId; private double totalPrice; + private double subTotalPrice; + private double promoCodeDiscounts; private ProductCartDTO product; private ItemSource source; private ItemCartStatus status; @@ -27,5 +30,6 @@ public class ItemCartDTO { private LocalDateTime expirationDate; private Long sessionId; private CountdownDTO countdown; - + private List<GiftCardDTO> giftCards; + private List<Long> giftCardIds; } diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/ItemRequestDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/ItemRequestDTO.java index 0887082db766a6a6d7d183cdfaba68e4de84ce7e..f9f645e204652dfdd2e7f505e1f5efa27ad9a145 100644 --- a/src/main/java/com/marketingconfort/mydressin/dtos/ItemRequestDTO.java +++ b/src/main/java/com/marketingconfort/mydressin/dtos/ItemRequestDTO.java @@ -1,11 +1,14 @@ package com.marketingconfort.mydressin.dtos; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.util.List; + @Setter @Getter @NoArgsConstructor @@ -17,6 +20,8 @@ public class ItemRequestDTO { private Long quantity; private ProductType type; private boolean useWebStockForLive; + private GiftCardDTO giftCardDTO; + public ItemRequestDTO(Long id, Long quantity, ProductType type) { this.productId=id; diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/PromoCodeDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/PromoCodeDTO.java deleted file mode 100644 index f6ec746b6f7a06946d97229739f870164d988fc3..0000000000000000000000000000000000000000 --- a/src/main/java/com/marketingconfort/mydressin/dtos/PromoCodeDTO.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.marketingconfort.mydressin.dtos; - -import com.marketingconfort.mydressin.common.Addons.dtos.CategoryDTO; -import com.marketingconfort.mydressin.common.Addons.enumerations.PromoCodeType; -import com.marketingconfort.mydressin.common.cart.dtos.ProductCartDTO; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.math.BigDecimal; -import java.time.LocalDate; -import java.util.List; - -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class PromoCodeDTO { - private Long id; - private String code; - private PromoCodeType promoCodeType; - private Integer valueCode; - private String description; - private List<Long> productsIncludedIds; - private List<Long> categoriesIncludedIds; - private List<ProductCartDTO> productsIncluded; - private List<CategoryDTO> categoriesIncluded; - private Integer usage; - private Integer usageLimit; - private LocalDate startDate; - private LocalDate expirationDate; - private BigDecimal minAmount; - private List<Long> productsExcludedIds; - private List<Long> categoriesExcludedIds; - private List<ProductCartDTO> productsExcluded; - private List<CategoryDTO> categoriesExcluded; - private Boolean freeShipping; - private Boolean uniquePromoCode; -} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/ChartDataDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/ChartDataDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..f5da0124748aff4e7c4e46bd8405846366e9c17e --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/ChartDataDTO.java @@ -0,0 +1,18 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class ChartDataDTO { + private List<String> labels; + private List<SeriesDataDTO> series; + +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DailyAmountDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DailyAmountDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..c7208eed19094fcac7dd53d4dccc74e71439ee47 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DailyAmountDTO.java @@ -0,0 +1,16 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDate; +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class DailyAmountDTO { + private LocalDate date; + private double amount; +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DateCountDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DateCountDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..55122c8b9d5d8b0d9b0f688378f5411daa583a4f --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/DateCountDTO.java @@ -0,0 +1,18 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import java.time.LocalDate; + + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class DateCountDTO { + private LocalDate date; + private Long count; + +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/SeriesDataDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/SeriesDataDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..475dae31d81ff3d9d71183b8daf373fb2c33c6da --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/SeriesDataDTO.java @@ -0,0 +1,18 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class SeriesDataDTO { + private String name; + private String type; + private String fill; + private List<Long> data; +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountRequestDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountRequestDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..abd9bb5be4871ce626eec004ba0542ede744140f --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountRequestDTO.java @@ -0,0 +1,19 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDateTime; +import java.util.List; +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class TheoreticalAmountRequestDTO { + private List<ItemSource> sources; + private LocalDateTime startDate; + private LocalDateTime endDate; +} diff --git a/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountResponseDTO.java b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountResponseDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..be06c4b425743e23816e7bc6431a3882191a6fb8 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/dtos/StatisticsDTOs/TheoreticalAmountResponseDTO.java @@ -0,0 +1,16 @@ +package com.marketingconfort.mydressin.dtos.StatisticsDTOs; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +public class TheoreticalAmountResponseDTO { + private List<DailyAmountDTO> dailyAmounts; + +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/BadRequestException.java b/src/main/java/com/marketingconfort/mydressin/exceptions/BadRequestException.java new file mode 100644 index 0000000000000000000000000000000000000000..8a2dc520105c22a34b56300465b5c49c05ac14fd --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/BadRequestException.java @@ -0,0 +1,11 @@ +package com.marketingconfort.mydressin.exceptions; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class BadRequestException extends RuntimeException { + private final String code; + private final String message; +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/ErrorResponse.java b/src/main/java/com/marketingconfort/mydressin/exceptions/ErrorResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..e6b56f3808fd107c1b18be80edc0969f5737817d --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/ErrorResponse.java @@ -0,0 +1,11 @@ +package com.marketingconfort.mydressin.exceptions; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public class ErrorResponse { + private final String code; + private final String message; +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalException.java b/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalException.java new file mode 100644 index 0000000000000000000000000000000000000000..053811648b894a1f1795fc60e65214bc5c4edb46 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalException.java @@ -0,0 +1,24 @@ +package com.marketingconfort.mydressin.exceptions; + +public class FunctionalException extends Exception { + private final FunctionalExceptionType type; + + public FunctionalException(FunctionalExceptionType type) { + super(type.getMessage()); + this.type = type; + } + + public FunctionalException(FunctionalExceptionType type, String customMessage) { + super(customMessage); + this.type = type; + } + + public FunctionalException(Exception cause) { + super(cause); + this.type = null; // Type non défini dans ce cas + } + + public FunctionalExceptionType getType() { + return type; + } +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalExceptionType.java b/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalExceptionType.java new file mode 100644 index 0000000000000000000000000000000000000000..117a8aca66caa83ee6959773098f09e22762bb7b --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/FunctionalExceptionType.java @@ -0,0 +1,22 @@ +package com.marketingconfort.mydressin.exceptions; + +public enum FunctionalExceptionType { + PROMO_CODE_NOT_FOUND("Promo code not found."), + PROMO_CODE_RETRIEVAL_ERROR("Erreur lors de la récupération du code promo."), + PROMO_CODE_LIST_RETRIEVAL_ERROR("Erreur lors de la récupération des codes promo."), + EMPTY_ADDONS_SERVICE_RESPONSE("The response from the addons service is empty."), + PROMO_CODE_USAGE_UPDATE_FAILED("Failed to update usage for promo code."), + + GIFT_CARD_NOT_FOUND("Gift card not found."), + VOUCHER_NOT_FOUND("Voucher not found."); + + private final String message; + + FunctionalExceptionType(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/GiftcardExpiredException.java b/src/main/java/com/marketingconfort/mydressin/exceptions/GiftcardExpiredException.java new file mode 100644 index 0000000000000000000000000000000000000000..3397b5bdcb86fda28e55093859abbffa32eff59b --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/GiftcardExpiredException.java @@ -0,0 +1,7 @@ +package com.marketingconfort.mydressin.exceptions; + +public class GiftcardExpiredException extends RuntimeException { + public GiftcardExpiredException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/GlobalExceptionHandler.java b/src/main/java/com/marketingconfort/mydressin/exceptions/GlobalExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..4e51f7aefed5bc1c5ee9438c4defff6a828edb08 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/GlobalExceptionHandler.java @@ -0,0 +1,53 @@ +package com.marketingconfort.mydressin.exceptions; +import com.amazonaws.services.accessanalyzer.model.ResourceNotFoundException; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + + + @ExceptionHandler(ResourceNotFoundException.class) + public ResponseEntity<?> resourceNotFoundHandling(ResourceNotFoundException exception) { + return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(DataIntegrityViolationException.class) + public ResponseEntity<?> dataIntegrityViolationHandling(DataIntegrityViolationException exception) { + return new ResponseEntity<>("Violation d'intégrité des données", HttpStatus.CONFLICT); + } + + @ExceptionHandler(Exception.class) + public ResponseEntity<?> globalExceptionHandling(Exception exception) { + return new ResponseEntity<>(exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } + + @ExceptionHandler(InsufficientBalanceException.class) + public ResponseEntity<String> handleInsufficientBalanceException(InsufficientBalanceException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } + + + @ExceptionHandler(BadRequestException.class) + public ResponseEntity<ErrorResponse> handleBadRequestException(BadRequestException ex) { + logger.error("BadRequestException: code={}, message={}", ex.getCode(), ex.getMessage()); + ErrorResponse errorResponse = new ErrorResponse(ex.getCode(), ex.getMessage()); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(TechnicalException.class) + public ResponseEntity<ErrorResponse> handleTechnicalException(TechnicalException ex) { + logger.error("TechnicalException: code={}, message={}", "TECHNICAL_ERROR", ex.getMessage()); + ErrorResponse errorResponse = new ErrorResponse("TECHNICAL_ERROR", ex.getMessage()); + return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST); + } + + +} diff --git a/src/main/java/com/marketingconfort/mydressin/exceptions/InsufficientBalanceException.java b/src/main/java/com/marketingconfort/mydressin/exceptions/InsufficientBalanceException.java new file mode 100644 index 0000000000000000000000000000000000000000..443a99a39a55887c2c596c2d1601201528944812 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/exceptions/InsufficientBalanceException.java @@ -0,0 +1,7 @@ +package com.marketingconfort.mydressin.exceptions; + +public class InsufficientBalanceException extends RuntimeException { + public InsufficientBalanceException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/com/marketingconfort/mydressin/mappers/CartMapper.java b/src/main/java/com/marketingconfort/mydressin/mappers/CartMapper.java index 046196a65311b1be904663c9aab75d60fc4ff78c..4542ae855b815bd2694e7626fe1e74b9fc80cc6d 100644 --- a/src/main/java/com/marketingconfort/mydressin/mappers/CartMapper.java +++ b/src/main/java/com/marketingconfort/mydressin/mappers/CartMapper.java @@ -1,18 +1,15 @@ package com.marketingconfort.mydressin.mappers; -import com.marketingconfort.mydressin.common.Addons.enumerations.PromoCodeType; import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; import com.marketingconfort.mydressin.common.cart.models.Cart; -import com.marketingconfort.mydressin.common.cart.models.ItemCart; import com.marketingconfort.mydressin.dtos.CartDTO; -import com.marketingconfort.mydressin.dtos.PromoCodeDTO; -import com.marketingconfort.mydressin.services.CodePromoClientService; +import com.marketingconfort.mydressin.services.ExternalApiService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; import lombok.AllArgsConstructor; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.stereotype.Service; -import java.util.List; +import java.math.BigDecimal; import java.util.stream.Collectors; @Service @@ -20,10 +17,10 @@ import java.util.stream.Collectors; @Component public class CartMapper { private ItemCartMapper itemCartMapper; - private CodePromoClientService codePromoClientService; + private ExternalApiService externalApiService; - public CartDTO toDTO(Cart cart) { + public CartDTO toDTO(Cart cart) throws TechnicalException { if (cart == null) { return null; } @@ -34,147 +31,27 @@ public class CartMapper { .filter(itemCart -> !itemCart.isExpired() && itemCart.getStatus() != ItemCartStatus.DELETED_SITE && itemCart.getStatus()!=ItemCartStatus.DELETED_BO) .map(itemCartMapper::toDTO) .collect(Collectors.toList())); - cartDTO.setTotalPrice(calculateCartTotalPrice(cart)); + cartDTO.setSubTotalPrice(cart.getSubTotalPrice()); + cartDTO.setTotalPrice(cart.getTotalPrice()); + cartDTO.setPromoCodeDiscounts(0); cartDTO.setInfos(cart.getInfos()); cartDTO.setErrorsMessages(cart.getErrorsMessages()); cartDTO.setPromoCodeIds(cart.getPromoCodeIds()); if (cart.getPromoCodes() != null) { - cartDTO.setPromoCodes(codePromoClientService.getPromoCodesByIds(cart.getPromoCodeIds())); + cartDTO.setPromoCodes(externalApiService.getPromoCodesByIds(cart.getPromoCodeIds())); } - return cartDTO; - } - - public double calculateCartTotalPrice(Cart cart) { - if (cart == null || cart.getItems() == null || cart.getItems().isEmpty()) { - return 0.0; - } - double basePrice = calculateCartBasePrice(cart); - - double totalDiscount = calculateTotalDiscount(cart); - - return Math.max(basePrice - totalDiscount, 0); - } - - public double calculateCartBasePrice(Cart cart) { - if (cart == null || cart.getItems() == null) { - return 0.0; - } - return cart.getItems().stream() - .mapToDouble(item -> calculateRegularPrice(item)) - .sum(); - } - - public double calculateRegularPrice(ItemCart itemCart) { - if (itemCart == null || itemCart.getProduct() == null) { - return 0.0; - } - double promoPrice = itemCart.getProduct().getPromoPrice(); - double regularPrice = itemCart.getProduct().getRegularPrice(); - return (promoPrice > 0 ? promoPrice : regularPrice) * itemCart.getQuantity(); - } - - - public double calculateTotalDiscount(Cart cart) { - double totalDiscount = 0.0; - - if (cart != null && cart.getPromoCodeIds() != null && !cart.getPromoCodeIds().isEmpty()) { - List<PromoCodeDTO> promoCodes = codePromoClientService.getPromoCodesByIds(cart.getPromoCodeIds()); - - if (promoCodes != null && !promoCodes.isEmpty()) { - for (PromoCodeDTO promoCode : promoCodes) { - if (promoCode != null) { - if (promoCode.getPromoCodeType() == PromoCodeType.DISCOUNT_PER_CART || - promoCode.getPromoCodeType() == PromoCodeType.PERCENTAGE_PER_CART) { - totalDiscount += applyPromoToCart(cart, promoCode); - } else if (promoCode.getPromoCodeType() == PromoCodeType.DISCOUNT_PER_PRODUCT) { - applyDiscountPerProduct(cart, promoCode); - } - } - } - } - } - return totalDiscount; - } - - public double applyPromoToCart(Cart cart, PromoCodeDTO promoCode) { - double discount = 0.0; - - if (cart != null && promoCode != null && promoCode.getPromoCodeType() != null) { - boolean hasEligibleProducts = cartHasEligibleItems(cart, promoCode); - - if (hasEligibleProducts) { - switch (promoCode.getPromoCodeType()) { - case PERCENTAGE_PER_CART: - discount = (calculateCartBasePrice(cart) * promoCode.getValueCode()) / 100; - break; - case DISCOUNT_PER_CART: - discount = promoCode.getValueCode(); - break; - default: - throw new IllegalArgumentException("Type de code promo inconnu : " + promoCode.getPromoCodeType()); - } - } + if (cart.getAppliedGiftCardCode() != null) { + cartDTO.setAppliedGiftCard(externalApiService.getGiftCardByCode(cart.getAppliedGiftCardCode())); } - return discount; - } - - public boolean cartHasEligibleItems(Cart cart, PromoCodeDTO promoCode) { - if (cart == null || promoCode == null || cart.getItems() == null) { - return false; + if (cart.getAppliedVoucherCode() != null) { + cartDTO.setAppliedVoucher(externalApiService.getVoucherByCode(cart.getAppliedVoucherCode())); } - return cart.getItems().stream().anyMatch(item -> isProductEligibleForPromo(item, promoCode)); - } - - public void applyDiscountPerProduct(Cart cart, PromoCodeDTO promoCode) { - if (cart != null && cart.getItems() != null) { - for (ItemCart item : cart.getItems()) { - double originalPrice = calculateRegularPrice(item); - - if (isProductEligibleForPromo(item, promoCode)) { - double discount = promoCode.getValueCode() * item.getQuantity(); - - double newTotalPrice = Math.max(0, originalPrice - discount); - item.setTotalPrice(newTotalPrice); - - double newUnitPrice = newTotalPrice / item.getQuantity(); - - if (item.getProduct().getPromoPrice() > 0) { - item.getProduct().setPromoPrice(newUnitPrice); - } else { - item.getProduct().setRegularPrice(newUnitPrice); - } - } - } - } - } + cartDTO.setAppliedGiftCardCode(cart.getAppliedGiftCardCode()); + cartDTO.setGiftCardBalanceUsed(cart.getGiftCardBalanceUsed()); + cartDTO.setAppliedVoucherCode(cart.getAppliedVoucherCode()); + cartDTO.setVoucherBalanceUsed(cart.getVoucherBalanceUsed()); - public boolean isProductEligibleForPromo(ItemCart item, PromoCodeDTO promoCode) { - if (item == null || promoCode == null || item.getProduct() == null) { - return false; - } - - boolean isProductIncluded = promoCode.getProductsIncludedIds().contains(item.getProductId()); - - boolean isCategoryIncluded = promoCode.getCategoriesIncludedIds().stream() - .anyMatch(categoryId -> item.getProduct().getCategoriesIds() != null - && item.getProduct().getCategoriesIds().contains(categoryId)); - - return isProductIncluded || isCategoryIncluded; - } - - public void removeInvalidPromoCodes(Cart cart) { - if (cart.getPromoCodeIds() != null && !cart.getPromoCodeIds().isEmpty()) { - List<PromoCodeDTO> promoCodes = codePromoClientService.getPromoCodesByIds(cart.getPromoCodeIds()); - - for (PromoCodeDTO promoCode : promoCodes) { - boolean hasEligibleProducts = cartHasEligibleItems(cart, promoCode); - - if (!hasEligibleProducts) { - cart.getPromoCodeIds().remove(promoCode.getId()); - cart.getInfos().add("Le code promo " + promoCode.getCode() + " a été retiré car aucun produit éligible n'est présent dans le panier."); - } - } - } + return cartDTO; } } diff --git a/src/main/java/com/marketingconfort/mydressin/mappers/ItemCartMapper.java b/src/main/java/com/marketingconfort/mydressin/mappers/ItemCartMapper.java index ecee3901061b3c6f7abe6301f83706ae02e921fa..5e1092bac1f31be1352a356926927a7abfdaa80a 100644 --- a/src/main/java/com/marketingconfort/mydressin/mappers/ItemCartMapper.java +++ b/src/main/java/com/marketingconfort/mydressin/mappers/ItemCartMapper.java @@ -1,9 +1,14 @@ package com.marketingconfort.mydressin.mappers; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; import com.marketingconfort.mydressin.common.cart.models.ItemCart; +import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; import com.marketingconfort.mydressin.dtos.ItemCartDTO; import org.springframework.stereotype.Component; +import java.math.BigDecimal; +import java.util.ArrayList; + @Component public class ItemCartMapper { @@ -20,26 +25,45 @@ public class ItemCartMapper { itemCartDTO.setStatus(itemCart.getStatus()); itemCartDTO.setExpired(itemCart.isExpired()); itemCartDTO.setExpirationDate(itemCart.getExpirationDate()); + if (itemCart.getProductType() == ProductType.GIFT_CARD) { + if (itemCart.getGiftCards() != null) { + itemCartDTO.setGiftCards(itemCart.getGiftCards()); + } else { + itemCartDTO.setGiftCards(new ArrayList<>()); + } + } else if (itemCart.getProduct() != null) { + itemCartDTO.setProduct(itemCart.getProduct()); + } if (itemCart.getOrder() != null && itemCart.getOrder().getSaleSession() != null) { itemCartDTO.setSessionId(itemCart.getOrder().getSaleSession().getId()); } - if (itemCart.getProduct() != null) { - itemCartDTO.setProduct(itemCart.getProduct()); - } - itemCartDTO.setProductType(itemCart.getProductType()); + itemCartDTO.setGiftCardIds(itemCart.getGiftCardIds()); calculateItemCartTotalPrice(itemCartDTO); + itemCartDTO.setPromoCodeDiscounts(0); return itemCartDTO; } private void calculateItemCartTotalPrice(ItemCartDTO itemCartDto) { - if (itemCartDto.getProduct() != null) { - double price = (itemCartDto.getProduct().getPromoPrice() != 0) ? itemCartDto.getProduct().getPromoPrice() : itemCartDto.getProduct().getRegularPrice(); + if (itemCartDto.getProductType() == ProductType.GIFT_CARD && itemCartDto.getGiftCards() != null) { + double subTotalPrice = itemCartDto.getGiftCards().stream() + .mapToDouble(GiftCardDTO::getSolde) + .sum(); + itemCartDto.setSubTotalPrice(subTotalPrice); + itemCartDto.setTotalPrice(subTotalPrice); + } else if (itemCartDto.getProduct() != null) { + double price = (itemCartDto.getProduct().getPromoPrice() != 0) + ? itemCartDto.getProduct().getPromoPrice() + : itemCartDto.getProduct().getRegularPrice(); + itemCartDto.setSubTotalPrice(price * itemCartDto.getQuantity()); itemCartDto.setTotalPrice(price * itemCartDto.getQuantity()); + } else { + itemCartDto.setSubTotalPrice(0.0); + itemCartDto.setTotalPrice(0.0); } } diff --git a/src/main/java/com/marketingconfort/mydressin/mappers/OrderMapper.java b/src/main/java/com/marketingconfort/mydressin/mappers/SessionOrderMapper.java similarity index 85% rename from src/main/java/com/marketingconfort/mydressin/mappers/OrderMapper.java rename to src/main/java/com/marketingconfort/mydressin/mappers/SessionOrderMapper.java index 508469dfc251ed76bca4f4ddbddde0798dd81a1b..7b0abb30e9ea6f32154f9f74ad649d9d237a7f00 100644 --- a/src/main/java/com/marketingconfort/mydressin/mappers/OrderMapper.java +++ b/src/main/java/com/marketingconfort/mydressin/mappers/SessionOrderMapper.java @@ -1,31 +1,30 @@ -package com.marketingconfort.mydressin.mappers; - -import com.marketingconfort.mydressin.common.cart.models.SessionOrder; -import com.marketingconfort.mydressin.dtos.SessionOrderDTO; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Service -public class OrderMapper { - private final ItemCartMapper itemCartMapper; - - public OrderMapper(ItemCartMapper itemCartMapper) { - this.itemCartMapper = itemCartMapper; - } - - public SessionOrderDTO toDTO(SessionOrder sessionOrder){ - if(sessionOrder==null){ - return null; - } - SessionOrderDTO orderDTO=new SessionOrderDTO(); - orderDTO.setId(sessionOrder.getId()); - orderDTO.setStatus(sessionOrder.getStatus()); - orderDTO.setClientId(sessionOrder.getClientId()); - orderDTO.setCreatedDate(sessionOrder.getCreatedDate()); - orderDTO.setLastModification(sessionOrder.getLastModification()); - orderDTO.setItemCartDTO(itemCartMapper.toDTO(sessionOrder.getItemCart())); - orderDTO.getItemCartDTO().setSessionId(sessionOrder.getSaleSession().getId()); - orderDTO.setPrice(sessionOrder.getOrderPrice()); - return orderDTO; - } -} +package com.marketingconfort.mydressin.mappers; + +import com.marketingconfort.mydressin.common.cart.models.SessionOrder; +import com.marketingconfort.mydressin.dtos.SessionOrderDTO; +import org.springframework.stereotype.Service; + +@Service +public class SessionOrderMapper { + private final ItemCartMapper itemCartMapper; + + public SessionOrderMapper(ItemCartMapper itemCartMapper) { + this.itemCartMapper = itemCartMapper; + } + + public SessionOrderDTO toDTO(SessionOrder sessionOrder){ + if(sessionOrder==null){ + return null; + } + SessionOrderDTO orderDTO=new SessionOrderDTO(); + orderDTO.setId(sessionOrder.getId()); + orderDTO.setStatus(sessionOrder.getStatus()); + orderDTO.setClientId(sessionOrder.getClientId()); + orderDTO.setCreatedDate(sessionOrder.getCreatedDate()); + orderDTO.setLastModification(sessionOrder.getLastModification()); + orderDTO.setItemCartDTO(itemCartMapper.toDTO(sessionOrder.getItemCart())); + orderDTO.getItemCartDTO().setSessionId(sessionOrder.getSaleSession().getId()); + orderDTO.setPrice(sessionOrder.getOrderPrice()); + return orderDTO; + } +} diff --git a/src/main/java/com/marketingconfort/mydressin/repositories/CartRepository.java b/src/main/java/com/marketingconfort/mydressin/repositories/CartRepository.java index 6a977210ee428cf600562874a3e2226af3bd60f7..2e73221dcc9fb473b93a13dbc6f6ce28246d86ba 100644 --- a/src/main/java/com/marketingconfort/mydressin/repositories/CartRepository.java +++ b/src/main/java/com/marketingconfort/mydressin/repositories/CartRepository.java @@ -1,19 +1,23 @@ package com.marketingconfort.mydressin.repositories; import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.enumurations.Status; import com.marketingconfort.mydressin.common.cart.models.Cart; import jakarta.persistence.LockModeType; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; @Repository -public interface CartRepository extends JpaRepository<Cart, Long> { +public interface CartRepository extends JpaRepository<Cart, Long>, JpaSpecificationExecutor<Cart> { @Lock(LockModeType.PESSIMISTIC_WRITE) Optional<Cart> findByClientIdAndStatus(Long clientId, Status status); @@ -23,7 +27,26 @@ public interface CartRepository extends JpaRepository<Cart, Long> { @Param("deletedBoStatus") ItemCartStatus deletedBoStatus); - int countByPromoCodeIds(Long promoCodeId); - - int countByPromoCodeIdsAndClientId(Long promoCodeId, Long clientId); + @Query("SELECT COUNT(DISTINCT c.id) FROM Cart c " + + "JOIN c.items i " + + "WHERE (:sources IS NULL OR str(i.source) IN :sources) " + + "AND (:startDate IS NULL OR c.createdDate >= :startDate) " + + "AND (:endDate IS NULL OR c.createdDate <= :endDate)") + Long countCartsBySourceAndDate( + @Param("sources") List<String> sources, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate + ); + + @Query(value = "SELECT CAST(c.created_date AS DATE) as date, SUM(c.sub_total_price) as totalAmount " + + "FROM cart c " + + "JOIN item_cart ic on c.id = ic.cart_id " + + "WHERE (:sources IS NULL OR ic.source IN (:sources)) " + + "AND c.created_date BETWEEN :startDate AND :endDate " + + "GROUP BY CAST(c.created_date AS DATE)", nativeQuery = true) + List<Object[]> sumCartsByDate( + @Param("sources") List<String> sources, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate + ); } diff --git a/src/main/java/com/marketingconfort/mydressin/repositories/DateCountProjection.java b/src/main/java/com/marketingconfort/mydressin/repositories/DateCountProjection.java new file mode 100644 index 0000000000000000000000000000000000000000..d3c2ebe9a6c1f90e1418c62df6e50d768fd9ef46 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/repositories/DateCountProjection.java @@ -0,0 +1,8 @@ +package com.marketingconfort.mydressin.repositories; + +import java.time.LocalDate; + +public interface DateCountProjection { + LocalDate getDate(); + Long getCount(); +} diff --git a/src/main/java/com/marketingconfort/mydressin/repositories/ItemCartRepository.java b/src/main/java/com/marketingconfort/mydressin/repositories/ItemCartRepository.java index da2e98da0d788cc8b3d22850b0f802755ee72f95..decc2e73fd41f8ae95cac365105de77c2e59720e 100644 --- a/src/main/java/com/marketingconfort/mydressin/repositories/ItemCartRepository.java +++ b/src/main/java/com/marketingconfort/mydressin/repositories/ItemCartRepository.java @@ -2,16 +2,48 @@ package com.marketingconfort.mydressin.repositories; import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.models.ItemCart; + +import java.time.LocalDateTime; import java.util.List; +import java.util.Map; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.DateCountDTO; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @Repository -public interface ItemCartRepository extends JpaRepository<ItemCart, Long> { +public interface ItemCartRepository extends JpaRepository<ItemCart, Long>, JpaSpecificationExecutor<ItemCart> { ItemCart findByProductIdAndCartId(Long productId, Long cartId); ItemCart findByIdAndCartId(Long id,Long cartId); List<ItemCart> findBySourceAndIsPayed(ItemSource source, boolean isPayed); + + + + @Query("SELECT i FROM ItemCart i WHERE (:sources IS NULL OR i.source IN :sources) AND (:startDate IS NULL OR i.addedDate >= :startDate) AND (:endDate IS NULL OR i.addedDate <= :endDate)") + List<ItemCart> findBySourceInAndAddedDateBetween(@Param("sources") List<ItemSource> sources, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate); + + @Query(value = "SELECT CAST(i.added_date AS DATE) as date, COUNT(*) as count " + + "FROM item_cart i " + + "WHERE i.added_date BETWEEN :startDate AND :endDate " + + "AND (:source IS NULL OR i.source = :source) " + + "GROUP BY CAST(i.added_date AS DATE) " + + "ORDER BY CAST(i.added_date AS DATE)", nativeQuery = true) + List<Object[]> countAddToCartByDateNative( + @Param("source") String source, + @Param("startDate") LocalDateTime startDate, + @Param("endDate") LocalDateTime endDate); } + + + + + + + diff --git a/src/main/java/com/marketingconfort/mydressin/services/CartService.java b/src/main/java/com/marketingconfort/mydressin/services/CartService.java index 5ed3eca8fbd588c0f5d8b879f44ed18bb19a3650..579de19d7c592a42ce8710bc6da0fe85f77c3eec 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/CartService.java +++ b/src/main/java/com/marketingconfort/mydressin/services/CartService.java @@ -1,10 +1,13 @@ package com.marketingconfort.mydressin.services; +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductStockDTO; import com.marketingconfort.mydressin.common.cart.models.Cart; import com.marketingconfort.mydressin.common.cart.models.ItemCart; +import com.marketingconfort.mydressin.dtos.ApplyPromoCodeRequest; +import com.marketingconfort.mydressin.dtos.ApplyPromoCodeResponse; import com.marketingconfort.mydressin.dtos.CartDTO; -import com.marketingconfort.mydressin.dtos.PromoCodeDTO; +import com.marketingconfort.starter.core.exceptions.TechnicalException; import java.util.List; @@ -24,25 +27,39 @@ public interface CartService { Cart findCartClient(Long clientId); - CartDTO reload(Long clientId); + CartDTO reload(Long clientId) throws TechnicalException; - CartDTO reload(Cart cart); + CartDTO reload(Cart cart) throws TechnicalException; - CartDTO getCartByClientId(Long clientId); + CartDTO getCartByClientId(Long clientId) throws TechnicalException; boolean deleteCartById(Long cartId); - CartDTO addProductsToCartFromLocalStorage(Long clientId, Cart cart); + CartDTO addProductsToCartFromLocalStorage(Long clientId, Cart cart) throws TechnicalException; void emptyCartByClientId(Long clientId); boolean isPromoCodeValid(PromoCodeDTO promoCodeDTO); - Cart applyPromoCodes(Cart cart); + List<ProductStockDTO> checkStockForAllCartProducts(Long clientId); List<ProductStockDTO> decrementStockForAllCartProducts(Long clientId); + CartDTO applyGiftcardToCart(Long clientId, String giftcardCode) throws TechnicalException; + + CartDTO removeGiftcardFromCart(Long clientId) throws TechnicalException; + + CartDTO applyVoucherToCart(Long clientId, String voucherCode) throws TechnicalException; + + CartDTO removeVoucherFromCart(Long clientId) throws TechnicalException; + + + + CartDTO checkAndApplyPromoCodeToCart(String promoCode, Long clientId) throws TechnicalException; + + CartDTO removeCodePromoFromCart(Long clientId, Long promoCodeIdToRemove) throws TechnicalException; + void removeInvalidPromoCodes(Cart cart) throws TechnicalException; } diff --git a/src/main/java/com/marketingconfort/mydressin/services/CodePromoClientService.java b/src/main/java/com/marketingconfort/mydressin/services/CodePromoClientService.java deleted file mode 100644 index 6bbe8bee34b336a1f2537893fff12166ec74d329..0000000000000000000000000000000000000000 --- a/src/main/java/com/marketingconfort/mydressin/services/CodePromoClientService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.marketingconfort.mydressin.services; - -import com.marketingconfort.mydressin.dtos.PromoCodeDTO; - -import java.util.List; - -public interface CodePromoClientService { - PromoCodeDTO getPromoCode(String code); - List<PromoCodeDTO> getPromoCodesByIds(List<Long> ids); -} diff --git a/src/main/java/com/marketingconfort/mydressin/services/CodePromoService.java b/src/main/java/com/marketingconfort/mydressin/services/CodePromoService.java deleted file mode 100644 index 673c56dd4e0831bdecf48b8fed2f2d3ad346b422..0000000000000000000000000000000000000000 --- a/src/main/java/com/marketingconfort/mydressin/services/CodePromoService.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.marketingconfort.mydressin.services; - -import com.marketingconfort.mydressin.common.cart.models.Cart; -import com.marketingconfort.mydressin.dtos.CartDTO; - -public interface CodePromoService { - CartDTO checkCodePromoByClientId(String code, Long clientId); - Cart checkCodePromoInCart(Cart cart, String codePromo); - CartDTO removeCodePromoFromCart(Long clientId, Long promoCodeIdToRemove); -} diff --git a/src/main/java/com/marketingconfort/mydressin/services/ExternalApiService.java b/src/main/java/com/marketingconfort/mydressin/services/ExternalApiService.java new file mode 100644 index 0000000000000000000000000000000000000000..9abc85e57f7345adb6122f41f5459614a16620e8 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/ExternalApiService.java @@ -0,0 +1,23 @@ +package com.marketingconfort.mydressin.services; + +import com.marketingconfort.mydressin.common.Addons.dtos.ProductCategoriesResponse; +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.common.Addons.models.Voucher; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; +import com.marketingconfort.mydressin.common.cart.dtos.VoucherDTO; +import com.marketingconfort.starter.core.exceptions.TechnicalException; + +import java.util.List; + +public interface ExternalApiService { + PromoCodeDTO getPromoCode(String code) throws TechnicalException; + PromoCodeDTO getPromoCodeById(Long promoCodeId) throws TechnicalException; + List<PromoCodeDTO> getPromoCodesByIds(List<Long> ids) throws TechnicalException; + List<ProductCategoriesResponse> getProductsAndVariationsWithCategories(List<Long> productIds, List<Long> variationIds) throws TechnicalException; + void updateUsagePromoCode(boolean incrementUsage, Long promoCodeId) throws TechnicalException; + GiftCardDTO loadGiftCardById(Long productId); + List<GiftCardDTO> loadGiftCardsByIds(List<Long> giftCardIds); + GiftCardDTO getGiftCardByCode(String code) throws TechnicalException; + VoucherDTO getVoucherByCode(String code) throws TechnicalException; + GiftCardDTO getGiftCardById(Long productId); +} diff --git a/src/main/java/com/marketingconfort/mydressin/services/ItemCartService.java b/src/main/java/com/marketingconfort/mydressin/services/ItemCartService.java index e431a1f73ff938ea9f1076ffe0c7d060eb301789..9456e33f80b7b41725682d682b21013699986976 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/ItemCartService.java +++ b/src/main/java/com/marketingconfort/mydressin/services/ItemCartService.java @@ -7,6 +7,7 @@ import com.marketingconfort.mydressin.common.cart.models.Cart; import com.marketingconfort.mydressin.common.cart.models.ItemCart; import com.marketingconfort.mydressin.dtos.CartDTO; import com.marketingconfort.mydressin.dtos.ItemRequestDTO; +import com.marketingconfort.starter.core.exceptions.TechnicalException; public interface ItemCartService { @@ -16,23 +17,25 @@ public interface ItemCartService { void updateExistingItemCartQuantity(ItemCart itemCart, Long quantity); - CartDTO addProductToCartByClientId(ItemRequestDTO itemRequest); + CartDTO addProductToCartByClientId(ItemRequestDTO itemRequest) throws TechnicalException; - CartDTO addProductToCart(Cart cart, ItemRequestDTO itemRequest); + CartDTO addProductToCart(Cart cart, ItemRequestDTO itemRequest) throws TechnicalException; - CartDTO updateItemCartById(Long ItemCartId, Long quantity); + CartDTO updateItemCartById(Long ItemCartId, Long quantity) throws TechnicalException; - CartDTO updateItemCartById(Cart cart, Long itemCartId, Long quantity); + CartDTO updateItemCartById(Cart cart, Long itemCartId, Long quantity) throws TechnicalException; - CartDTO deleteItemCartById(Long clientId, Long itemCartId); + CartDTO deleteItemCartById(Long clientId, Long itemCartId) throws TechnicalException; - CartDTO deleteItemCartById(Cart cart, Long itemCartId); + CartDTO deleteItemCartById(Cart cart, Long itemCartId) throws TechnicalException; - CartDTO deleteItemCartByIdFromBO(Long clientId, Long itemCartId); + CartDTO deleteItemCartByIdFromBO(Long clientId, Long itemCartId) throws TechnicalException; - CartDTO deleteItemCartByIdFromBO(Cart cart, Long itemCartId); + CartDTO deleteItemCartByIdFromBO(Cart cart, Long itemCartId) throws TechnicalException; void transferCartToUser(Long unregisteredClientId, Long newClientId); + CartDTO addGiftCardToCart(Cart cart, ItemRequestDTO itemRequest) throws TechnicalException; + } diff --git a/src/main/java/com/marketingconfort/mydressin/services/PromoCodeUtilService.java b/src/main/java/com/marketingconfort/mydressin/services/PromoCodeUtilService.java new file mode 100644 index 0000000000000000000000000000000000000000..f9ab6f3fddd5c4f86458f25f1a1190f01d54e501 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/PromoCodeUtilService.java @@ -0,0 +1,17 @@ +package com.marketingconfort.mydressin.services; + +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.dtos.CartDTO; +import com.marketingconfort.mydressin.dtos.ItemCartDTO; +import com.marketingconfort.starter.core.exceptions.TechnicalException; + + +public interface PromoCodeUtilService { + CartDTO calculatePromoCodeDiscountsToCart(CartDTO cartDTO) throws TechnicalException; + boolean validateCartItemsForPromoCode(CartDTO cartDTO, PromoCodeDTO promoCodeDTO); + boolean isItemIncludedOrExcluded(ItemCartDTO item, PromoCodeDTO promoCodeDTO); + boolean isPromoCodeReducingCartTotal(CartDTO cart, PromoCodeDTO promoCodeDTO); + double applyDiscountPerProduct(PromoCodeDTO promoCodeDTO, ItemCartDTO item); + double applyDiscountPerCart(PromoCodeDTO promoCodeDTO, CartDTO cart); + double applyPercentagePerCart(PromoCodeDTO promoCodeDTO, CartDTO cart); +} diff --git a/src/main/java/com/marketingconfort/mydressin/services/StatisticsService.java b/src/main/java/com/marketingconfort/mydressin/services/StatisticsService.java new file mode 100644 index 0000000000000000000000000000000000000000..a92c5a5139fd3698a9bbd75353e0df773fd277f6 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/StatisticsService.java @@ -0,0 +1,21 @@ +package com.marketingconfort.mydressin.services; + +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.ChartDataDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.DailyAmountDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.TheoreticalAmountRequestDTO; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.TheoreticalAmountResponseDTO; +import com.marketingconfort.starter.core.exceptions.TechnicalException; + +import java.time.LocalDateTime; +import java.util.List; + +public interface StatisticsService { + double calculateTheoreticalRevenue(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate) throws TechnicalException; + ChartDataDTO getAddToCartTrends(List<ItemSource> sources) throws TechnicalException; + Long getTotalCarts(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate); + List<DailyAmountDTO> calculateTheoreticalAmounts(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate); + TheoreticalAmountResponseDTO getTheoreticalAmountResponse(TheoreticalAmountRequestDTO requestDTO); + + + } \ No newline at end of file diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/CartServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/CartServiceImp.java index 99c87d80d2156e71b0036fe391e4ed5a8bfeb903..e803618ae9de05970c435142971f25bbefc7e672 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/CartServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/CartServiceImp.java @@ -2,50 +2,74 @@ package com.marketingconfort.mydressin.services.impl; import com.marketingconfort.mydressin.Utils.CartUtil; +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductCartDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductStockDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductStockRequestDTO; +import com.marketingconfort.mydressin.common.cart.dtos.*; import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.enumurations.OrderStatus; import com.marketingconfort.mydressin.common.cart.enumurations.Status; import com.marketingconfort.mydressin.common.cart.models.Cart; import com.marketingconfort.mydressin.common.cart.models.ItemCart; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUri; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUrl; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.Path.AddonsServicePath; +import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; import com.marketingconfort.mydressin.dtos.*; +import com.marketingconfort.mydressin.dtos.CartDTO; +import com.marketingconfort.mydressin.dtos.ItemCartDTO; import com.marketingconfort.mydressin.exceptions.CartNotFoundException; +import com.marketingconfort.mydressin.exceptions.GiftcardExpiredException; +import com.marketingconfort.mydressin.exceptions.InsufficientBalanceException; import com.marketingconfort.mydressin.mappers.CartMapper; import com.marketingconfort.mydressin.repositories.CartRepository; -import com.marketingconfort.mydressin.services.CartCountConfigService; -import com.marketingconfort.mydressin.services.CartService; -import com.marketingconfort.mydressin.services.CodePromoClientService; -import com.marketingconfort.mydressin.services.ProductStockService; -import lombok.AllArgsConstructor; +import com.marketingconfort.mydressin.services.*; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import com.marketingconfort.starter.core.services.MCRestTemplateService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; import java.time.Duration; -import java.time.LocalDate; import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; -@AllArgsConstructor @Service @Transactional public class CartServiceImp implements CartService { - private CartRepository cartRepository; - private ProductStockService productStockService; - private CartMapper cartMapper; + private final CartRepository cartRepository; + private final ProductStockService productStockService; + private final CartMapper cartMapper; private static final int NEAR_EXPIRATION_THRESHOLD_DAYS = 3; - private CodePromoClientService codePromoClientService; - @Autowired - private CartCountConfigService cartCountConfigService; + private final CartCountConfigService cartCountConfigService; + private static final Logger logger = LoggerFactory.getLogger(CartServiceImp.class); + private final MCRestTemplateService mcRestTemplateService; + private final NameUrl nameUrl; + private final ExternalApiService externalApiService; + private final PromoCodeUtilService promoCodeUtilService; + + public CartServiceImp(CartRepository cartRepository, ProductStockService productStockService, CartMapper cartMapper, CartCountConfigService cartCountConfigService, + MCRestTemplateService mcRestTemplateService, NameUrl nameUrl, ExternalApiService externalApiService, PromoCodeUtilService promoCodeUtilService) { + this.cartRepository = cartRepository; + this.productStockService = productStockService; + this.cartMapper = cartMapper; + this.cartCountConfigService = cartCountConfigService; + this.mcRestTemplateService = mcRestTemplateService; + this.nameUrl = nameUrl; + this.externalApiService = externalApiService; + this.promoCodeUtilService = promoCodeUtilService; + } - private static final Logger logger = LoggerFactory.getLogger(CodePromoClientServiceImp.class); @Transactional @Override @@ -54,7 +78,6 @@ public class CartServiceImp implements CartService { return optionalCart.orElseGet(() -> createNewCartClient(clientId)); } - @Override public Cart createNewCartClient(Long clientId) { Cart cart = new Cart(); @@ -63,15 +86,46 @@ public class CartServiceImp implements CartService { return cartRepository.save(cart); } + @Override + public Cart findCartClient(Long clientId) { + Cart cart = cartRepository.findCartWithValidItems(clientId, Status.open, ItemCartStatus.DELETED_SITE, ItemCartStatus.DELETED_BO) + .orElseThrow(() -> { + logger.error("No cart found for client ID: {}", clientId); + return new CartNotFoundException("Panier non trouvé pour le client ID: " + clientId); + }); + + calculateCartTotalPrice(cart); + + return cart; + } + @Override public double calculateCartTotalPrice(Cart cart) { if (cart == null || cart.getItems() == null || cart.getItems().isEmpty()) { + logger.info("Cart is empty or null."); + return 0.0; + } + + // Filter valid items + List<ItemCart> validItems = cart.getItems().stream() + .filter(itemCart -> !itemCart.isExpired() && !itemCart.isPayed() + && itemCart.getStatus() != ItemCartStatus.DELETED_BO + && itemCart.getStatus() != ItemCartStatus.DELETED_SITE) + .collect(Collectors.toList()); + + if (validItems.isEmpty()) { + logger.info("No valid items in cart."); return 0.0; } - List<ItemCart> missingProductItems = cart.getItems().stream() - .filter(item -> item.getProduct() == null) + + logger.info("Valid items in cart: {}", validItems); + + // Find items that are missing product details + List<ItemCart> missingProductItems = validItems.stream() + .filter(item -> item.getProduct() == null && item.getProductType() != ProductType.GIFT_CARD) .toList(); + // Fetch product details for missing products if (!missingProductItems.isEmpty()) { List<ProductRequestDTO> productRequestDTOS = missingProductItems.stream() .filter(item -> item.getProductId() != null) @@ -79,12 +133,13 @@ public class CartServiceImp implements CartService { .collect(Collectors.toList()); if (!productRequestDTOS.isEmpty()) { + logger.info("Fetching product details for items: {}", productRequestDTOS); List<ProductCartDTO> productCartDTOS = productStockService.getProducts(productRequestDTOS); if (productCartDTOS != null && !productCartDTOS.isEmpty()) { productCartDTOS.forEach(productCartDTO -> { if (productCartDTO != null && productCartDTO.getProductId() != null) { - cart.getItems().stream() + validItems.stream() .filter(item -> productCartDTO.getProductId().equals(item.getProductId())) .forEach(item -> item.setProduct(productCartDTO)); } @@ -92,14 +147,133 @@ public class CartServiceImp implements CartService { } } } - double basePrice = cartMapper.calculateCartBasePrice(cart); - double totalDiscount = cartMapper.calculateTotalDiscount(cart); + List<ItemCart> giftCardItems = validItems.stream() + .filter(item -> item.getProductType() == ProductType.GIFT_CARD && (item.getGiftCards() == null || item.getGiftCards().isEmpty())) + .toList(); + + if (!giftCardItems.isEmpty()) { + for (ItemCart item : giftCardItems) { + if (item.getGiftCardIds() != null && !item.getGiftCardIds().isEmpty()) { + List<GiftCardDTO> giftCards = loadGiftCardsByIds(item.getGiftCardIds()); + item.setGiftCards(giftCards); + } else { + validItems.remove(item); + cart.getInfos().add("Les cartes cadeaux associées au produit ID " + item.getProductId() + " n'ont pas pu être chargées et ont été retirées du panier."); + } + } + } + + double newSubTotalPrice = calculateCartBasePriceFromItems(validItems); + cart.setSubTotalPrice(newSubTotalPrice); + cart.setTotalPrice(newSubTotalPrice); + + return newSubTotalPrice; + } + + public double calculateCartBasePriceFromItems(List<ItemCart> items) { + if (items == null || items.isEmpty()) { + return 0.0; + } + return items.stream() + .mapToDouble(this::calculateRegularPrice) + .sum(); + } + + public double calculateRegularPrice(ItemCart itemCart) { + if (itemCart == null) { + return 0.0; + } + if (itemCart.getProductType() == ProductType.GIFT_CARD && itemCart.getGiftCards() != null) { + return itemCart.getGiftCards().stream() + .mapToDouble(GiftCardDTO::getSolde) + .sum(); + } else if (itemCart.getProduct() != null) { + double promoPrice = itemCart.getProduct().getPromoPrice(); + double regularPrice = itemCart.getProduct().getRegularPrice(); + return (promoPrice > 0 ? promoPrice : regularPrice) * itemCart.getQuantity(); + } else { + return 0.0; + } + } + + private void calculateVoucherDiscount(CartDTO cartDTO) { + if (cartDTO.getAppliedVoucher() != null) { + double voucherBalance = cartDTO.getAppliedVoucher().getAmount(); + double amountToUse = Math.min(cartDTO.getTotalPrice(), voucherBalance); + cartDTO.setVoucherBalanceUsed((long) amountToUse); + cartDTO.setTotalPrice(cartDTO.getTotalPrice() - amountToUse); + } else { + cartDTO.setVoucherBalanceUsed(0L); + } + } - double finalPrice = basePrice - totalDiscount; - cart.setTotalPrice(finalPrice); + private void calculateGiftCardDiscount(CartDTO cartDTO) { + if (cartDTO.getAppliedGiftCardCode() != null) { + long giftCardBalance = cartDTO.getAppliedGiftCard().getSolde(); + double amountToUse = Math.min(cartDTO.getTotalPrice(), giftCardBalance); + cartDTO.setGiftCardBalanceUsed((long) amountToUse); + cartDTO.setTotalPrice(cartDTO.getTotalPrice() - amountToUse); + } else { + cartDTO.setGiftCardBalanceUsed(0L); + } + } + + public void calculateVoucherThenGiftCardDiscounts(CartDTO cartDTO) { + Cart cart = cartRepository.findCartWithValidItems(cartDTO.getClientId(), Status.open, ItemCartStatus.DELETED_SITE, ItemCartStatus.DELETED_BO) + .orElseThrow(() -> { + logger.error("No cart found for client ID: {}", cartDTO.getClientId()); + return new CartNotFoundException("Panier non trouvé pour le client ID: " + cartDTO.getClientId()); + }); - return finalPrice; + if (cart.getAppliedGiftCardCode() != null && cart.getAppliedGiftCard() == null) { + fetchAndSetAppliedGiftCard(cart); + } + + if (cart.getAppliedVoucherCode() != null && cart.getAppliedVoucher() == null) { + fetchAndSetAppliedVoucher(cart); + } + + calculateVoucherDiscount(cartDTO); + calculateGiftCardDiscount(cartDTO); + + cart.setTotalPrice(cartDTO.getTotalPrice()); + cart.setVoucherBalanceUsed(cartDTO.getVoucherBalanceUsed()); + cart.setGiftCardBalanceUsed(cartDTO.getGiftCardBalanceUsed()); + cartRepository.save(cart); + } + + + @Override + public CartDTO reload(Cart cart) throws TechnicalException { + cart.getItems().forEach(item -> { + if (item.getStatus() == ItemCartStatus.NOT_CONSULTED) { + item.setStatus(ItemCartStatus.CONSULTED); + } + }); + updateCartWithStockAvailability(cart); + calculateCartTotalPrice(cart); + removeInvalidPromoCodes(cart); + + CartDTO cartDTO = cartMapper.toDTO(cart); + + Optional<CartCountConfigDTO> configOptional = cartCountConfigService.findFirstConfig(); + CartCountConfigDTO config = configOptional.orElse(null); + applyCountdownToCartDTO(cartDTO, config); + + promoCodeUtilService.calculatePromoCodeDiscountsToCart(cartDTO); + calculateVoucherThenGiftCardDiscounts(cartDTO); + + cart.getInfos().clear(); + cart.getErrorsMessages().clear(); + cartRepository.save(cart); + return cartDTO; + } + + @Override + public CartDTO getCartByClientId(Long clientId) throws TechnicalException { + Cart cart = findCartClient(clientId); + return reload(cart); } @Override @@ -123,6 +297,8 @@ public class CartServiceImp implements CartService { cart.getItems().stream() .filter(item -> (item.getProductId().equals(currentProductId) && item.getSource() == stockResult.getSource())) + .filter(itemCart -> itemCart.getProductType() != ProductType.GIFT_CARD) + .findFirst() .ifPresent(itemCart -> { if (stockResult.isAvailable()) { @@ -144,7 +320,6 @@ public class CartServiceImp implements CartService { return cart; } - private List<ProductStockRequestDTO> collectValidProducts(Cart cart) { return cart.getItems().stream() @@ -158,7 +333,6 @@ public class CartServiceImp implements CartService { .collect(Collectors.toList()); } - @Override public void checkLiveItemExpiration(ItemCart itemCart, Cart cart) { @@ -183,7 +357,6 @@ public class CartServiceImp implements CartService { } } - private boolean isItemNearExpiration(ItemCart itemCart) { return itemCart.getExpirationDate() != null && itemCart.getExpirationDate().isBefore(LocalDateTime.now().plusDays(NEAR_EXPIRATION_THRESHOLD_DAYS)); @@ -194,60 +367,6 @@ public class CartServiceImp implements CartService { itemCart.getExpirationDate().isBefore(LocalDateTime.now()); } - - @Override - public Cart findCartClient(Long clientId) { - Cart cart = cartRepository.findCartWithValidItems(clientId, Status.open, ItemCartStatus.DELETED_SITE, ItemCartStatus.DELETED_BO) - .orElseThrow(() -> new CartNotFoundException("Panier non trouvé pour le client ID: " + clientId)); - - calculateCartTotalPrice(cart); - - return cart; - } - - - @Override - public CartDTO reload(Long clientId) { - Cart cart = findCartClient(clientId); - reload(cart); - CartDTO cartDTO = cartMapper.toDTO(cart); - cartRepository.save(cart); - - return cartDTO; - } - - - @Override - public CartDTO reload(Cart cart) { - cart.getItems().forEach(item -> { - if (item.getStatus() == ItemCartStatus.NOT_CONSULTED) { - item.setStatus(ItemCartStatus.CONSULTED); - } - }); - - updateCartWithStockAvailability(cart); - applyPromoCodes(cart); - return cartMapper.toDTO(cart); - } - - - @Override - public CartDTO getCartByClientId(Long clientId) { - Cart cart = findCartClient(clientId); - reload(cart); - - Optional<CartCountConfigDTO> configOptional = cartCountConfigService.findFirstConfig(); - CartCountConfigDTO config = configOptional.orElse(null); - - CartDTO cartDTO = cartMapper.toDTO(cart); - - applyCountdownToCartDTO(cartDTO, config); - - cartRepository.save(cart); - - return cartDTO; - } - private void applyCountdownToCartDTO(CartDTO cartDTO, CartCountConfigDTO config) { if (config != null) { if (config.isDontShowCountCart()) { @@ -316,7 +435,6 @@ public class CartServiceImp implements CartService { return new CountdownDTO(days, hours, minutes, seconds); } - @Override public boolean deleteCartById(Long cartId) { Cart cart = cartRepository.findById(cartId) @@ -329,7 +447,7 @@ public class CartServiceImp implements CartService { } @Override - public CartDTO addProductsToCartFromLocalStorage(Long clientId, Cart localCart) { + public CartDTO addProductsToCartFromLocalStorage(Long clientId, Cart localCart) throws TechnicalException { if (localCart == null || localCart.getItems() == null || localCart.getItems().isEmpty()) { return null; @@ -389,46 +507,10 @@ public class CartServiceImp implements CartService { cartRepository.save(cart); } - @Override - public boolean isPromoCodeValid(PromoCodeDTO promoCodeDTO) { - - if (promoCodeDTO.getStartDate() != null && promoCodeDTO.getExpirationDate() != null) { - LocalDate today = LocalDate.now(); - return !today.isBefore(promoCodeDTO.getStartDate()) && - !today.isAfter(promoCodeDTO.getExpirationDate()); - } - return false; - } - - @Override - public Cart applyPromoCodes(Cart cart) { - if (cart != null) { - if (cart.getPromoCodeIds() != null && !cart.getPromoCodeIds().isEmpty()) { - List<PromoCodeDTO> promoCodes = codePromoClientService.getPromoCodesByIds(cart.getPromoCodeIds()); - List<Long> validPromoCodeIds = new ArrayList<>(); - if (promoCodes != null) { - for (PromoCodeDTO promoCode : promoCodes) { - if (isPromoCodeValid(promoCode)) { - validPromoCodeIds.add(promoCode.getId()); - } else { - cart.getErrorsMessages().add("Code Promo " + promoCode.getId() + " n'est plus valide et a été supprimé."); - } - } - cart.setPromoCodeIds(validPromoCodeIds); - } - } - } - return cart; - } - @Override @Transactional public List<ProductStockDTO> checkStockForAllCartProducts(Long clientId) { - Cart cart = cartRepository.findCartWithValidItems(clientId, Status.open, ItemCartStatus.DELETED_SITE, ItemCartStatus.DELETED_BO) - .orElseThrow(() -> { - logger.error("No cart found for client ID: {}", clientId); - return new CartNotFoundException("Panier non trouvé pour le client ID: " + clientId); - }); + Cart cart = findCartClient(clientId); List<ProductStockRequestDTO> productStockRequestDTOList = collectValidProducts(cart); @@ -439,11 +521,7 @@ public class CartServiceImp implements CartService { public List<ProductStockDTO> decrementStockForAllCartProducts(Long clientId) { logger.info("Attempting to decrement stock for all cart products for client ID: {}", clientId); - Cart cart = cartRepository.findCartWithValidItems(clientId, Status.open, ItemCartStatus.DELETED_SITE, ItemCartStatus.DELETED_BO) - .orElseThrow(() -> { - logger.error("No cart found for client ID: {}", clientId); - return new CartNotFoundException("Panier non trouvé pour le client ID: " + clientId); - }); + Cart cart = findCartClient(clientId); logger.info("Cart retrieved successfully for client ID: {}", clientId); @@ -470,4 +548,309 @@ public class CartServiceImp implements CartService { } } + + + /* *************** Gift Card Methods ************ */ + private void fetchAndSetAppliedGiftCard(Cart cart) { + String giftcardCode = cart.getAppliedGiftCardCode(); + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardByCode(giftcardCode); + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + + if (response.getStatusCode() == HttpStatus.OK) { + GiftCardDTO giftcardDTO = response.getBody(); + cart.setAppliedGiftCard(giftcardDTO); + } else { + cart.setAppliedGiftCardCode(null); + } + } + + private GiftCardDTO loadGiftCardById(Long productId) { + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardById(productId); + + try { + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new RuntimeException("Failed to retrieve gift card with ID: " + productId); + } + } catch (Exception e) { + throw new RuntimeException("Error while fetching gift card with ID: " + productId, e); + } + } + + private List<GiftCardDTO> loadGiftCardsByIds(List<Long> giftCardIds) { + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.GET_GIFTCARDS_BY_IDS; + + + try { + logger.info("Fetching gift cards for IDs: {}",giftCardIds ); + + HttpEntity<List<Long>> requestEntity = new HttpEntity<>(giftCardIds); + ResponseEntity<GiftCardDTO[]> response = mcRestTemplateService.postForObject( + url, requestEntity, GiftCardDTO[].class); + if (response.getStatusCode() == HttpStatus.OK) { + return Arrays.asList(response.getBody()); + } else { + throw new RuntimeException("Failed to retrieve gift cards with IDs: " + giftCardIds); + } + } catch (Exception e) { + throw new RuntimeException("Error while fetching gift cards with IDs: " + giftCardIds, e); + } + } + + @Override + @Transactional + public CartDTO applyGiftcardToCart(Long clientId, String giftcardCode) throws TechnicalException { + Cart cart = findCartClient(clientId); + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardByCode(giftcardCode); + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + + if (response.getStatusCode() != HttpStatus.OK) { + throw new CartNotFoundException("Carte cadeau invalide ou non trouvée."); + } + + GiftCardDTO giftcardDTO = response.getBody(); + + if (giftcardDTO.getDateexpiration() != null && giftcardDTO.getDateexpiration().isBefore(LocalDateTime.now())) { + throw new GiftcardExpiredException("La carte cadeau est expirée."); + } + + if (giftcardDTO.getSolde() <= 0) { + throw new InsufficientBalanceException("Le solde de la carte cadeau est insuffisant."); + } + + cart.setAppliedGiftCard(giftcardDTO); + cart.setAppliedGiftCardCode(giftcardCode); + + cartRepository.save(cart); + + CartDTO cartDTO = getCartByClientId(clientId); + cartDTO.getInfos().add("Carte cadeau appliqué avec succès."); + + return cartDTO; + } + + @Override + @Transactional + public CartDTO removeGiftcardFromCart(Long clientId) throws TechnicalException { + Cart cart = findCartClient(clientId); + + cart.setAppliedGiftCard(null); + cart.setAppliedGiftCardCode(null); + cart.setGiftCardBalanceUsed(0L); + + cartRepository.save(cart); + + CartDTO cartDTO = getCartByClientId(clientId); + cartDTO.getInfos().add("Carte cadeau supprimé avec succès."); + + return cartDTO; + } + /* *************** Gift Card Methods ************ */ + + + + /* *************** Voucher Methods ************ */ + private void fetchAndSetAppliedVoucher(Cart cart) { + String voucherCode = cart.getAppliedVoucherCode(); + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getVoucherByCode(voucherCode); + ResponseEntity<VoucherDTO> response = mcRestTemplateService.getForObject(url, VoucherDTO.class); + + if (response.getStatusCode() == HttpStatus.OK) { + VoucherDTO voucherDTO = response.getBody(); + cart.setAppliedVoucher(voucherDTO); + } else { + cart.setAppliedGiftCardCode(null); + } + } + + @Override + @Transactional + public CartDTO applyVoucherToCart(Long clientId, String voucherCode) throws TechnicalException { + Cart cart = findCartClient(clientId); + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getVoucherByCode(voucherCode); + ResponseEntity<VoucherDTO> response = mcRestTemplateService.getForObject(url, VoucherDTO.class); + + if (response.getStatusCode() != HttpStatus.OK) { + throw new CartNotFoundException("Carte cadeau invalide ou non trouvée."); + } + + VoucherDTO voucherDTO = response.getBody(); + + if (voucherDTO.getExpirationDate() != null && voucherDTO.getExpirationDate().isBefore(LocalDateTime.now())) { + throw new GiftcardExpiredException("Le bon d'achat est expirée."); + } + + if (voucherDTO.getAmount() <= 0) { + throw new InsufficientBalanceException("Le solde du bon d'achat est insuffisant."); + } + + cart.setAppliedVoucher(voucherDTO); + cart.setAppliedVoucherCode(voucherCode); + + cartRepository.save(cart); + + CartDTO cartDTO = getCartByClientId(clientId); + cartDTO.getInfos().add("Bon d'achat appliqué avec succès."); + + return cartDTO; + } + + @Override + @Transactional + public CartDTO removeVoucherFromCart(Long clientId) throws TechnicalException { + Cart cart = findCartClient(clientId); + + cart.setAppliedVoucher(null); + cart.setAppliedVoucherCode(null); + cart.setVoucherBalanceUsed(0L); + + cartRepository.save(cart); + + CartDTO cartDTO = getCartByClientId(clientId); + cartDTO.getInfos().add("Bon d'achat supprimé avec succès."); + + return cartDTO; + } + /* *************** Voucher Methods ************ */ + + + + /* *************** Promo Code Methods ************ */ + @Override + public CartDTO checkAndApplyPromoCodeToCart(String promoCode, Long clientId) throws TechnicalException { + if (promoCode == null || promoCode.isEmpty() || clientId == null || clientId < 0) { + return null; + } + + CartDTO cartDTO = getCartByClientId(clientId); + PromoCodeDTO resultPromoCode = externalApiService.getPromoCode(promoCode); + + if (resultPromoCode == null) { + cartDTO.getErrorsMessages().add("Code Promo non trouvé : " + promoCode); + return cartDTO; + } + if (cartDTO.getPromoCodeIds().contains(resultPromoCode.getId())) { + cartDTO.getErrorsMessages().add("Code Promo déjà appliqué : " + promoCode); + return cartDTO; + } + if (resultPromoCode.getUsageLimit() != 0 && resultPromoCode.getUsage() >= resultPromoCode.getUsageLimit()) { + cartDTO.getErrorsMessages().add("Limite d'utilisation globale du Code Promo atteinte"); + return cartDTO; + } + if (resultPromoCode.getExpirationDate().isBefore(LocalDateTime.now())) { + logger.warn("Promo code {} is expired.", resultPromoCode.getCode()); + cartDTO.getErrorsMessages().add("Code Promo expiré : " + promoCode); + return cartDTO; + } + if (resultPromoCode.getUniquePromoCode() && !cartDTO.getPromoCodeIds().isEmpty()) { + cartDTO.getErrorsMessages().add("Code Promo unique non cumulable avec d'autres codes promo : " + promoCode); + return cartDTO; + } + List<PromoCodeDTO> existingPromoCodes = externalApiService.getPromoCodesByIds(cartDTO.getPromoCodeIds()); + boolean hasUniquePromoCode = existingPromoCodes.stream().anyMatch(PromoCodeDTO::getUniquePromoCode); + if (hasUniquePromoCode) { + cartDTO.getErrorsMessages().add("Un autre Code Promo unique est déjà appliqué au panier"); + return cartDTO; + } + if (resultPromoCode.getMinAmount().compareTo(BigDecimal.valueOf(cartDTO.getTotalPrice())) > 0) { + cartDTO.getErrorsMessages().add("Montant minimum requis pour appliquer le Code Promo non atteint : " + promoCode + " : " + resultPromoCode.getMinAmount() + "€"); + return cartDTO; + } + boolean itemsEligible = promoCodeUtilService.validateCartItemsForPromoCode(cartDTO, resultPromoCode); + if (!itemsEligible) { + cartDTO.getErrorsMessages().add("Aucun produit éligible pour ce Code Promo : " + promoCode); + return cartDTO; + } + boolean isReducingTotal = promoCodeUtilService.isPromoCodeReducingCartTotal(cartDTO, resultPromoCode); + if (!isReducingTotal) { + cartDTO.getErrorsMessages().add("Le Code Promo n'a pas réduit le montant total du panier : " + promoCode); + return cartDTO; + } + + Cart cart = findCartClient(clientId); + cart.getPromoCodeIds().add(resultPromoCode.getId()); + cartRepository.save(cart); + + CartDTO finalCartDTO = reload(cart); + finalCartDTO.getInfos().add("Code promo appliqué avec succès : " + promoCode); + finalCartDTO.getPromoCodeIds().add(resultPromoCode.getId()); + + return finalCartDTO; + } + + @Override + public CartDTO removeCodePromoFromCart(Long clientId, Long promoCodeIdToRemove) throws TechnicalException { + Cart cart = findCartClient(clientId); + + if (cart == null) { + throw new IllegalArgumentException("Le panier pour ce client n'existe pas."); + } + + if (cart.getPromoCodeIds() == null || cart.getPromoCodeIds().isEmpty()) { + throw new IllegalArgumentException("Aucun code promo à supprimer du panier."); + } + + if (!cart.getPromoCodeIds().contains(promoCodeIdToRemove)) { + throw new IllegalArgumentException("Le code promo spécifié n'est pas appliqué au panier."); + } + + cart.getPromoCodeIds().removeIf(id -> id.equals(promoCodeIdToRemove)); + cartRepository.save(cart); + + CartDTO cartDTO = reload(cart); + + PromoCodeDTO promoCodeDTO = externalApiService.getPromoCodeById(promoCodeIdToRemove); + cartDTO.getInfos().add("Le code promo: " + promoCodeDTO.getCode() + " est supprimé avec succès."); + + return cartDTO; + } + + @Override + public void removeInvalidPromoCodes(Cart cart) throws TechnicalException { + if (cart.getPromoCodeIds() != null && !cart.getPromoCodeIds().isEmpty()) { + List<PromoCodeDTO> promoCodes = externalApiService.getPromoCodesByIds(cart.getPromoCodeIds()); + + for (PromoCodeDTO promoCode : promoCodes) { + CartDTO cartDTO = cartMapper.toDTO(cart); + boolean hasValidateCartItems = promoCodeUtilService.validateCartItemsForPromoCode(cartDTO, promoCode); + boolean isPromoCodeReducingCartTotal = promoCodeUtilService.isPromoCodeReducingCartTotal(cartDTO, promoCode); + boolean isCartValid = isPromoCodeReducingCartTotal && hasValidateCartItems; + + if (!isCartValid) { + cart.getPromoCodeIds().remove(promoCode.getId()); + } + } + cartRepository.save(cart); + } + } + /* *************** Promo Code Methods ************ */ + + + + @Override + public CartDTO reload(Long clientId) throws TechnicalException { + Cart cart = findCartClient(clientId); + reload(cart); + CartDTO cartDTO = cartMapper.toDTO(cart); + cartRepository.save(cart); + + return cartDTO; + } + + @Override + public boolean isPromoCodeValid(PromoCodeDTO promoCodeDTO) { + + if (promoCodeDTO.getStartDate() != null && promoCodeDTO.getExpirationDate() != null) { + LocalDateTime today = LocalDateTime.now(); + return !today.isBefore(promoCodeDTO.getStartDate()) && + !today.isAfter(promoCodeDTO.getExpirationDate()); + } + return false; + } + } diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoClientServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoClientServiceImp.java deleted file mode 100644 index 9da67e4253187c0b385f252c0ca779eaffba0878..0000000000000000000000000000000000000000 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoClientServiceImp.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.marketingconfort.mydressin.services.impl; - -import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUri; -import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUrl; -import com.marketingconfort.mydressin.common.constant.urlServiceConstant.Path.AddonsServicePath; -import com.marketingconfort.mydressin.dtos.PromoCodeDTO; -import com.marketingconfort.mydressin.services.CodePromoClientService; -import com.marketingconfort.starter.core.services.MCRestTemplateService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestClientException; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; - -@Service -public class CodePromoClientServiceImp implements CodePromoClientService { - - private final MCRestTemplateService mcRestTemplateService; - private static final Logger logger = LoggerFactory.getLogger(CodePromoClientServiceImp.class); - private final NameUrl nameUrl; - - - public CodePromoClientServiceImp(MCRestTemplateService mcRestTemplateService, NameUrl nameUrl) { - this.mcRestTemplateService = mcRestTemplateService; - this.nameUrl = nameUrl; - } - - @Override - public PromoCodeDTO getPromoCode(String code) { - String url = nameUrl.getAddonsService()+ NameUri.ADDONS + AddonsServicePath.getPromoCodeUrl(code); - try { - logger.info("Envoi de la requête pour récupérer le code promo: {}", code); - ResponseEntity<PromoCodeDTO> response = mcRestTemplateService.getForObject(url, PromoCodeDTO.class); - if (response.getStatusCode() == HttpStatus.OK) { - logger.info("Code promo récupéré avec succès: {}", response.getBody()); - return response.getBody(); - } else { - logger.warn("Erreur lors de la récupération du code promo : {}", response.getStatusCode()); - throw new RuntimeException("Erreur lors de la récupération du code promo : " + response.getStatusCode()); - } - } catch (RestClientException e) { - logger.error("Erreur de communication avec le microservice pour le code promo '{}': {}", code, e.getMessage(), e); - throw new RuntimeException("Erreur de communication avec le microservice", e); - } - } - - @Override - public List<PromoCodeDTO> getPromoCodesByIds(List<Long> ids) { - String url = nameUrl.getAddonsService()+ NameUri.ADDONS + AddonsServicePath.getPromoCodesByIdsUrl(ids); - try { - logger.info("Envoi de la requête pour récupérer les codes promo avec les IDs: {}", ids); - ResponseEntity<PromoCodeDTO[]> response = mcRestTemplateService.getForObject(url, PromoCodeDTO[].class); - if (response.getStatusCode() == HttpStatus.OK) { - logger.info("Codes promo récupérés avec succès, nombre de codes: {}", response.getBody() != null ? response.getBody().length : 0); - return Arrays.asList(Objects.requireNonNull(response.getBody())); - } else { - logger.warn("Erreur lors de la récupération des codes promo : {}", response.getStatusCode()); - throw new RuntimeException("Erreur lors de la récupération des codes promo : " + response.getStatusCode()); - } - } catch (RestClientException e) { - logger.error("Erreur de communication avec le microservice pour les codes promo: {}", e.getMessage(), e); - throw new RuntimeException("Erreur de communication avec le microservice", e); - } - } - -} - diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoServiceImp.java deleted file mode 100644 index c212136b3086e639939f6f8b171ba6de19b63dab..0000000000000000000000000000000000000000 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/CodePromoServiceImp.java +++ /dev/null @@ -1,169 +0,0 @@ -package com.marketingconfort.mydressin.services.impl; - -import com.marketingconfort.mydressin.common.cart.models.Cart; -import com.marketingconfort.mydressin.dtos.CartDTO; -import com.marketingconfort.mydressin.dtos.PromoCodeDTO; -import com.marketingconfort.mydressin.exceptions.CartNotFoundException; -import com.marketingconfort.mydressin.mappers.CartMapper; -import com.marketingconfort.mydressin.repositories.CartRepository; -import com.marketingconfort.mydressin.services.CartService; -import com.marketingconfort.mydressin.services.CodePromoClientService; -import com.marketingconfort.mydressin.services.CodePromoService; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.math.BigDecimal; -import java.util.List; - - -@Service -public class CodePromoServiceImp implements CodePromoService { - - private final CartRepository cartRepository; - - private final CodePromoClientService codePromoClientService; - - private final CartService cartService; - - private final CartMapper cartMapper; - - @Autowired - public CodePromoServiceImp(CartRepository cartRepository, - CodePromoClientService codePromoClientService, - CartService cartService, - CartMapper cartMapper) { - this.cartRepository = cartRepository; - this.codePromoClientService = codePromoClientService; - this.cartService = cartService; - this.cartMapper = cartMapper; - } - - - @Transactional - @Override - public CartDTO checkCodePromoByClientId(String code, Long clientId) { - Cart cart = cartService.findOrCreateCartByClientId(clientId); - if (cart == null) { - throw new CartNotFoundException("Cart not found for client id: " + clientId); - } - cart = checkCodePromoInCart(cart, code); - if (cart == null) { - return null; - } - cartService.reload(cart); - CartDTO cartDTO = cartMapper.toDTO(cart); - cartRepository.save(cart); - return cartDTO; - } - - @Transactional - @Override - public Cart checkCodePromoInCart(Cart cart, String codePromo) { - if (cart != null) { - PromoCodeDTO resultPromoCode = codePromoClientService.getPromoCode(codePromo); - if (resultPromoCode != null) { - double total = cartService.calculateCartTotalPrice(cart); - cart.setTotalPrice(total); - if (resultPromoCode.getUsageLimit() != 0 && getTotalUsage(resultPromoCode.getId()) >= resultPromoCode.getUsageLimit()) { - cart.getErrorsMessages().add("Limite d'utilisation du Code Promo atteinte"); - return cart; - } - if (resultPromoCode.getUsage() != 0 && getUsageCountByUser(resultPromoCode.getId(), cart.getClientId()) >= resultPromoCode.getUsage()) { - cart.getErrorsMessages().add("Limite d'utilisation du Code Promo par utilisateur atteinte"); - return cart; - } - if (!cartService.isPromoCodeValid(resultPromoCode)) { - cart.getErrorsMessages().add("Code Promo expiré"); - return cart; - } - if (cart.getPromoCodeIds().contains(resultPromoCode.getId())) { - cart.getErrorsMessages().add("Code Promo déjà appliqué"); - return cart; - } - if (resultPromoCode.getUniquePromoCode() && !cart.getPromoCodeIds().isEmpty()) { - cart.getErrorsMessages().add("PromoCode is not applicable with other promo codes."); - return cart; - } - - List<PromoCodeDTO> existingPromoCodes = codePromoClientService.getPromoCodesByIds(cart.getPromoCodeIds()); - - boolean hasUniquePromoCode = existingPromoCodes.stream() - .anyMatch(PromoCodeDTO::getUniquePromoCode); - - if (hasUniquePromoCode) { - cart.getErrorsMessages().add("Un Code Promo unique est déjà appliqué. Aucun autre code promo ne peut être ajouté."); - return cart; - } - if (resultPromoCode.getMinAmount().compareTo(BigDecimal.valueOf(cart.getTotalPrice())) > 0) { - cart.getErrorsMessages().add("Code Promo doit être appliqué sur une quantité minimale de " + resultPromoCode.getMinAmount() + ". Total du panier actuel : " + cart.getTotalPrice()); - return cart; - } - boolean hasEligibleProducts = cartMapper.cartHasEligibleItems(cart, resultPromoCode); - - if (!hasEligibleProducts) { - cart.getErrorsMessages().add("Le code promo " + resultPromoCode.getCode() + " n'applique aucune réduction car aucun produit du panier n'est inclus dans ce code."); - return cart; - } - cart.getPromoCodeIds().add(resultPromoCode.getId()); - cart.getInfos().add("Code Promo appliqué avec succès"); - return cart; - } - cart.getErrorsMessages().add("Code Promo non trouvé"); - return cart; - } - return null; - } - - - private int getTotalUsage(Long promoCodeId) { - return cartRepository.countByPromoCodeIds(promoCodeId); - } - - private int getUsageCountByUser(Long promoCodeId, Long ClientId) { - - return cartRepository.countByPromoCodeIdsAndClientId(promoCodeId, ClientId); - } - - @Transactional - @Override - public CartDTO removeCodePromoFromCart(Long clientId, Long promoCodeIdToRemove) { - Cart cart = cartService.findOrCreateCartByClientId(clientId); - if (cart == null) { - throw new IllegalArgumentException("Le panier pour ce client n'existe pas."); - } - - if (cart.getPromoCodeIds() == null || cart.getPromoCodeIds().isEmpty()) { - throw new IllegalArgumentException("Aucun code promo à supprimer du panier."); - } - - if (!cart.getPromoCodeIds().contains(promoCodeIdToRemove)) { - throw new IllegalArgumentException("Le code promo spécifié n'est pas appliqué au panier."); - } - - cart.getPromoCodeIds().remove(promoCodeIdToRemove); - - cartService.reload(cart); - cartRepository.save(cart); - CartDTO cartDTO = cartMapper.toDTO(cart); - cartDTO.getInfos().add("Code Promo supprimé avec succès."); - - return cartDTO; - } - -} - - - - - - - - - - - - - - - diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/ExternalApiServiceImpl.java b/src/main/java/com/marketingconfort/mydressin/services/impl/ExternalApiServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..e0e0631d94fa3bae0809b7177dba24097293184d --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/ExternalApiServiceImpl.java @@ -0,0 +1,251 @@ +package com.marketingconfort.mydressin.services.impl; + +import com.marketingconfort.mydressin.common.Addons.dtos.ProductCategoriesResponse; +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; +import com.marketingconfort.mydressin.common.cart.dtos.VoucherDTO; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUri; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUrl; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.Path.AddonsServicePath; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.Path.StockManagerServicePath; +import com.marketingconfort.mydressin.exceptions.FunctionalException; +import com.marketingconfort.mydressin.exceptions.FunctionalExceptionType; +import com.marketingconfort.mydressin.services.ExternalApiService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import com.marketingconfort.starter.core.services.MCRestTemplateService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClientException; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Service +public class ExternalApiServiceImpl implements ExternalApiService { + + + private final MCRestTemplateService mcRestTemplateService; + private static final Logger logger = LoggerFactory.getLogger(ExternalApiServiceImpl.class); + private final NameUrl nameUrl; + + + public ExternalApiServiceImpl(MCRestTemplateService mcRestTemplateService, NameUrl nameUrl) { + this.mcRestTemplateService = mcRestTemplateService; + this.nameUrl = nameUrl; + } + + @Override + public PromoCodeDTO getPromoCode(String code) throws TechnicalException { + String url = nameUrl.getAddonsService()+ NameUri.ADDONS + AddonsServicePath.getPromoCodeByCodeUrl(code); + try { + logger.info("Envoi de la requête pour récupérer le code promo avec code: {}", code); + ResponseEntity<PromoCodeDTO> response = mcRestTemplateService.getForObject(url, PromoCodeDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + logger.info("Code promo récupéré avec succès: {}", response.getBody()); + return response.getBody(); + } else { + logger.warn("Erreur lors de la récupération du code promo avec code : {}", response.getStatusCode()); + throw new FunctionalException(FunctionalExceptionType.PROMO_CODE_RETRIEVAL_ERROR); + } + } catch (RestClientException e) { + logger.error("Erreur de communication avec le microservice pour le code promo avec code '{}': {}", code, e.getMessage(), e); + throw new TechnicalException("Erreur de communication avec le microservice" + e.getMessage()); + } catch (FunctionalException e) { + throw new RuntimeException(e); + } + } + + @Override + public PromoCodeDTO getPromoCodeById(Long promoCodeId) throws TechnicalException { + String url = nameUrl.getAddonsService()+ NameUri.ADDONS + AddonsServicePath.getPromoCodeByIdUrl(promoCodeId); + try { + logger.info("Envoi de la requête pour récupérer le code promo ID: {}", promoCodeId); + ResponseEntity<PromoCodeDTO> response = mcRestTemplateService.getForObject(url, PromoCodeDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + logger.info("Code promo récupéré avec succès: {}", response.getBody()); + return response.getBody(); + } else { + logger.warn("Erreur lors de la récupération du code promo ID : {}", response.getStatusCode()); + throw new FunctionalException(FunctionalExceptionType.PROMO_CODE_RETRIEVAL_ERROR); + } + } catch (RestClientException e) { + logger.error("Erreur de communication avec le microservice pour le code promo ID '{}': {}", promoCodeId, e.getMessage(), e); + throw new TechnicalException("Erreur de communication avec le microservice" + e.getMessage()); + } catch (FunctionalException e) { + throw new RuntimeException(e); + } + } + + @Override + public List<PromoCodeDTO> getPromoCodesByIds(List<Long> ids) throws TechnicalException { + String url = nameUrl.getAddonsService()+ NameUri.ADDONS + AddonsServicePath.getPromoCodesByIdsUrl(ids); + try { + logger.info("Envoi de la requête pour récupérer les codes promo avec les IDs: {}", ids); + ResponseEntity<PromoCodeDTO[]> response = mcRestTemplateService.getForObject(url, PromoCodeDTO[].class); + if (response.getStatusCode() == HttpStatus.OK) { + logger.info("Codes promo récupérés avec succès, nombre de codes: {}", response.getBody() != null ? response.getBody().length : 0); + return Arrays.asList(Objects.requireNonNull(response.getBody())); + } else { + logger.warn("Erreur lors de la récupération des codes promo : {}", response.getStatusCode()); + throw new FunctionalException(FunctionalExceptionType.PROMO_CODE_LIST_RETRIEVAL_ERROR); + } + } catch (RestClientException | FunctionalException e) { + logger.error("Erreur de communication avec le microservice pour les codes promo: {}", e.getMessage(), e); + throw new TechnicalException("Erreur de communication avec le microservice" + e.getMessage()); + } + } + + @Override + public List<ProductCategoriesResponse> getProductsAndVariationsWithCategories(List<Long> productIds, List<Long> variationIds) throws TechnicalException { + String url = nameUrl.getStockManagementService() + NameUri.STOCK + StockManagerServicePath.getProductsVariationsWithCategoriesUrl(productIds, variationIds); + logger.info("Fetching product and variation categories for product IDs: {} and variation IDs: {}", productIds, variationIds); + + try { + // Préparer la requête + Map<String, List<Long>> requestPayload = Map.of( + "productIDs", productIds, + "variationIDs", variationIds + ); + + HttpEntity<Map<String, List<Long>>> request = new HttpEntity<>(requestPayload); + ResponseEntity<ProductCategoriesResponse[]> response = mcRestTemplateService.postForObject( + url, + request, + ProductCategoriesResponse[].class + ); + + if (response.getBody() != null) { + logger.info("Successfully retrieved product and variation categories: {}", (Object) response.getBody()); + return Arrays.asList(response.getBody()); + } else { + logger.error("The response from the addons service is empty."); + throw new FunctionalException(FunctionalExceptionType.EMPTY_ADDONS_SERVICE_RESPONSE); + } + } catch (Exception e) { + logger.error("Error calling addons service: {}", e.getMessage(), e); + throw new TechnicalException("Error calling addons service: " + e.getMessage()); + } + } + + @Override + public void updateUsagePromoCode(boolean incrementUsage, Long promoCodeId) throws TechnicalException { + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getUpdateUsagePromoCodeUrl(incrementUsage, promoCodeId); + String action = incrementUsage ? "incrementing" : "decrementing"; + + logger.info("Calling update usage promo code API for {} usage with URL: {} and parameters - promoCodeId: {}", action, url, promoCodeId); + try { + ResponseEntity<String> response = mcRestTemplateService.getForObject(url, String.class); + if (response.getStatusCode().is2xxSuccessful()) { + logger.info("Successfully {} usage for promo code ID: {}. Response: {}", action, promoCodeId, response.getBody()); + response.getBody(); + } else { + logger.error("Failed to {} usage for promo code ID: {}. Response: {}", action, promoCodeId, response); + throw new FunctionalException(FunctionalExceptionType.PROMO_CODE_USAGE_UPDATE_FAILED); + } + } catch (Exception e) { + logger.error("Error while {} usage for promo code ID: {}. Error: {}", action, promoCodeId, e.getMessage(), e); + throw new TechnicalException("Error while " + action + " usage for promo code: " + e.getMessage()); + } + } + + @Override + public GiftCardDTO loadGiftCardById(Long productId) { + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardById(productId); + + try { + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new RuntimeException("Failed to retrieve gift card with ID: " + productId); + } + } catch (Exception e) { + throw new RuntimeException("Error while fetching gift card with ID: " + productId, e); + } + } + + @Override + public List<GiftCardDTO> loadGiftCardsByIds(List<Long> giftCardIds) { + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.GET_GIFTCARDS_BY_IDS; + + + try { + logger.info("Fetching gift cards for IDs: {}",giftCardIds ); + + HttpEntity<List<Long>> requestEntity = new HttpEntity<>(giftCardIds); + ResponseEntity<GiftCardDTO[]> response = mcRestTemplateService.postForObject( + url, requestEntity, GiftCardDTO[].class); + if (response.getStatusCode() == HttpStatus.OK) { + return Arrays.asList(response.getBody()); + } else { + throw new RuntimeException("Failed to retrieve gift cards with IDs: " + giftCardIds); + } + } catch (Exception e) { + throw new RuntimeException("Error while fetching gift cards with IDs: " + giftCardIds, e); + } + } + + @Override + public GiftCardDTO getGiftCardByCode(String code) throws TechnicalException { + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardByCode(code); + + try { + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new FunctionalException(FunctionalExceptionType.GIFT_CARD_NOT_FOUND); + } + } catch (Exception e) { + throw new TechnicalException("Error while fetching gift card with code: " + code); + } + } + + @Override + public VoucherDTO getVoucherByCode(String code) throws TechnicalException { + + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getVoucherByCode(code); + + try { + ResponseEntity<VoucherDTO> response = mcRestTemplateService.getForObject(url, VoucherDTO.class); + if (response.getStatusCode() == HttpStatus.OK) { + return response.getBody(); + } else { + throw new FunctionalException(FunctionalExceptionType.VOUCHER_NOT_FOUND); + } + } catch (Exception e) { + throw new TechnicalException("Error while fetching voucher with code: " + code); + } + } + + public GiftCardDTO getGiftCardById(Long productId) { + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.getGiftCardById(productId); + + try { + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.getForObject(url, GiftCardDTO.class); + HttpStatus statusCode = (HttpStatus) response.getStatusCode(); + + if (statusCode == HttpStatus.OK) { + return response.getBody(); + } else if (statusCode == HttpStatus.NOT_FOUND) { + logger.warn("GiftCard not found with ID: {}. Skipping.", productId); + return null; + } else { + logger.error("Failed to retrieve gift card with ID: {}. HTTP Status: {}", productId, statusCode); + return null; + } + } catch (Exception e) { + logger.error("Exception while fetching gift card with ID: {}. Error: {}", productId, e.getMessage()); + return null; + } + } + +} diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/ItemCartServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/ItemCartServiceImp.java index 7e044bddb1889b225c0a977a4a7fcbcc267376bf..4fb6e1dfdef6330de2d92b72ef1ad8ab30e0e0df 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/ItemCartServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/ItemCartServiceImp.java @@ -1,6 +1,7 @@ package com.marketingconfort.mydressin.services.impl; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; import com.marketingconfort.mydressin.common.cart.dtos.ProductStockDTO; @@ -8,6 +9,10 @@ import com.marketingconfort.mydressin.common.cart.dtos.ProductStockRequestDTO; import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.models.*; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUri; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.NameUrl; +import com.marketingconfort.mydressin.common.constant.urlServiceConstant.Path.AddonsServicePath; +import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; import com.marketingconfort.mydressin.dtos.CartDTO; @@ -22,16 +27,28 @@ import com.marketingconfort.mydressin.services.CartService; import com.marketingconfort.mydressin.services.ItemCartService; import com.marketingconfort.mydressin.services.ProductStockService; import com.marketingconfort.mydressin.services.UnregisteredCartService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import com.marketingconfort.starter.core.services.MCRestTemplateService; import lombok.AllArgsConstructor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; + import java.time.LocalDateTime; import java.util.*; +import java.util.stream.Collectors; @AllArgsConstructor @Service public class ItemCartServiceImp implements ItemCartService { + private final MCRestTemplateService mcRestTemplateService; + private final NameUrl nameUrl; + private CartService cartService; private CartRepository cartRepository; private ItemCartRepository itemCartRepository; @@ -39,6 +56,7 @@ public class ItemCartServiceImp implements ItemCartService { private CartMapper cartMapper; private UnregisteredCartService unregisteredCartService; private UnregisteredCartRepository unregisteredCartRepository; + private static final Logger logger = LoggerFactory.getLogger(ItemCartServiceImp.class); @Override @@ -59,13 +77,20 @@ public class ItemCartServiceImp implements ItemCartService { } else if (cart instanceof UnregisteredCart) { itemCart.setUnregisteredCart((UnregisteredCart) cart); } - if (cart.getId() != null && cartRepository.existsById(cart.getId())) { - // If the cart exists, save the item - itemCartRepository.save(itemCart); + + if (itemCart.getProductType() == ProductType.GIFT_CARD && itemCart.getGiftCards() != null) { + double totalSolde = itemCart.getGiftCards().stream() + .mapToDouble(GiftCardDTO::getSolde) + .sum(); + itemCart.setTotalPrice(totalSolde); + } else if (itemCart.getProduct() != null) { + itemCart.setTotalPrice(itemCart.getProduct().getPromoPrice() * itemCart.getQuantity()); } + return itemCart; } + @Override public void updateExistingItemCartQuantity(ItemCart existingItemCart, Long quantity) { long newQuantity = existingItemCart.getQuantity() + quantity; @@ -77,16 +102,15 @@ public class ItemCartServiceImp implements ItemCartService { } @Override - public CartDTO addProductToCartByClientId(ItemRequestDTO itemRequest) { + public CartDTO addProductToCartByClientId(ItemRequestDTO itemRequest) throws TechnicalException { Cart cart = cartService.findOrCreateCartByClientId(itemRequest.getClientId()); addProductToCart(cart, itemRequest); cartRepository.save(cart); - return cartMapper.toDTO(cart); - + return cartService.reload(cart); } @Override - public CartDTO addProductToCart(Cart cart, ItemRequestDTO itemRequest) { + public CartDTO addProductToCart(Cart cart, ItemRequestDTO itemRequest) throws TechnicalException { if (cart == null) { return null; } @@ -95,42 +119,120 @@ public class ItemCartServiceImp implements ItemCartService { } else { cart.setInfos(new ArrayList<>()); } - cart = cartService.updateCartWithStockAvailability(cart); - ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO(itemRequest.getProductId(), itemRequest.getQuantity(), ItemSource.WEB, itemRequest.getType()); - ProductStockDTO stockResult = productStockService.checkStockWeb(productStockRequestDTO); - - if (!stockResult.isAvailable()) { - - cart.getInfos().add(stockResult.getProductDTO().getName() + " non disponible"); + if (itemRequest.getType() == ProductType.GIFT_CARD) { + return addGiftCardToCart(cart, itemRequest); } else { - Optional<ItemCart> optionalItemCart = cart.getItems().stream() - .filter(itemCart -> itemCart.getProductId().equals(itemRequest.getProductId()) && itemCart.getSource() == ItemSource.WEB) - .findFirst(); - - ItemCart itemCart; - if (optionalItemCart.isPresent()) { - itemCart = optionalItemCart.get(); - updateExistingItemCartQuantity(itemCart, itemRequest.getQuantity()); - itemCart.setStatus(ItemCartStatus.NOT_CONSULTED); + ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO( + itemRequest.getProductId(), + itemRequest.getQuantity(), + ItemSource.WEB, + itemRequest.getType() + ); + ProductStockDTO stockResult = productStockService.checkStockWeb(productStockRequestDTO); + if (!stockResult.isAvailable()) { + cart.getInfos().add(stockResult.getProductDTO().getName() + " non disponible"); } else { - itemCart = createNewItemCart(itemRequest, cart, ItemSource.WEB); - itemCart.setProduct(stockResult.getProductDTO()); - cart.getItems().add(itemCart); - cart.setItemsCount(cart.getItemsCount() + 1); + Optional<ItemCart> optionalItemCart = cart.getItems().stream() + .filter(itemCart -> itemCart.getProductId().equals(itemRequest.getProductId()) && itemCart.getSource() == ItemSource.WEB) + .findFirst(); + + ItemCart itemCart; + if (optionalItemCart.isPresent()) { + itemCart = optionalItemCart.get(); + updateExistingItemCartQuantity(itemCart, itemRequest.getQuantity()); + itemCart.setStatus(ItemCartStatus.NOT_CONSULTED); + } else { + itemCart = createNewItemCart(itemRequest, cart, ItemSource.WEB); + itemCart.setProduct(stockResult.getProductDTO()); + cart.getItems().add(itemCart); + cart.setItemsCount(cart.getItemsCount() + 1); + } + cart.getInfos().add(stockResult.getProductDTO().getName() + " ajouté avec succès"); } - cart.getInfos().add(stockResult.getProductDTO().getName() + " " + "ajouté avec succès"); - } - cartService.applyPromoCodes(cart); + + double total = cartService.calculateCartTotalPrice(cart); cart.setTotalPrice(total); - return cartMapper.toDTO(cart); + cartRepository.save(cart); + return cartService.reload(cart); + } + + + @Override + public CartDTO addGiftCardToCart(Cart cart, ItemRequestDTO itemRequest) throws TechnicalException { + if (cart == null) { + return null; + } + cart.getInfos().clear(); + + GiftCardDTO giftCardDTO = itemRequest.getGiftCardDTO(); + if (giftCardDTO == null) { + cart.getInfos().add("Les détails de la carte cadeau sont manquants"); + return cartService.reload(cart); + } + + List<GiftCardDTO> createdGiftCards = new ArrayList<>(); + + for (int i = 0; i < itemRequest.getQuantity(); i++) { + try { + String url = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.CEATE_GIFT_CARD; + HttpEntity<GiftCardDTO> requestEntity = new HttpEntity<>(giftCardDTO); + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.postForObject(url, requestEntity, GiftCardDTO.class); + + if (response.getStatusCode() == HttpStatus.CREATED) { + GiftCardDTO createdGiftCard = response.getBody(); + createdGiftCards.add(createdGiftCard); + } else { + cart.getInfos().add("Échec de la création de la carte cadeau numéro " + (i + 1)); + } + } catch (Exception e) { + cart.getInfos().add("Erreur lors de la création de la carte cadeau numéro " + (i + 1) + " : " + e.getMessage()); + } + } + + if (!createdGiftCards.isEmpty()) { + ItemCart itemCart = createNewGiftCardItemCart(itemRequest, cart, createdGiftCards); + cart.getItems().add(itemCart); + cart.setItemsCount(cart.getItemsCount() + 1); + cart.getInfos().add("Cartes cadeaux ajoutées avec succès"); + } + + return cartService.reload(cart); + } + + private ItemCart createNewGiftCardItemCart(ItemRequestDTO itemRequest, Cart cart, List<GiftCardDTO> giftCards) { + ItemCart itemCart = new ItemCart(); + itemCart.setQuantity((long) giftCards.size()); + itemCart.setProductId(giftCards.get(0).getGiftcardId()); + itemCart.setAddedDate(LocalDateTime.now()); + itemCart.setSource(ItemSource.WEB); + itemCart.setPayed(false); + itemCart.setExpired(false); + itemCart.setProductType(ProductType.GIFT_CARD); + itemCart.setStatus(ItemCartStatus.NOT_CONSULTED); + itemCart.setGiftCards(giftCards); + itemCart.setCart(cart); + itemCart.setGiftCardIds(giftCards.stream() + .map(GiftCardDTO::getGiftcardId) + .collect(Collectors.toList())); + + double totalSolde = giftCards.stream() + .mapToDouble(GiftCardDTO::getSolde) + .sum(); + itemCart.setTotalPrice(totalSolde); + + return itemCart; } + + + + @Override - public CartDTO updateItemCartById(Long itemCartId, Long quantity) { + public CartDTO updateItemCartById(Long itemCartId, Long quantity) throws TechnicalException { ItemCart itemCart = itemCartRepository.findById(itemCartId) .orElseThrow(() -> new ItemNotFoundException("ItemCart not found with ID: " + itemCartId)); @@ -138,69 +240,132 @@ public class ItemCartServiceImp implements ItemCartService { Cart cart = itemCart.getCart(); updateItemCartById(cart, itemCartId, quantity); cartRepository.save(cart); - return cartMapper.toDTO(cart); + return cartService.reload(cart); } @Override - public CartDTO updateItemCartById(Cart cart, Long itemCartId, Long quantity) { + public CartDTO updateItemCartById(Cart cart, Long itemCartId, Long quantity) throws TechnicalException { if (cart == null) { return null; } - if (cart.getInfos() != null && !cart.getInfos().isEmpty()) { - cart.getInfos().clear(); - } else { - cart.setInfos(new ArrayList<>()); - } + cart.getInfos().clear(); cart = cartService.updateCartWithStockAvailability(cart); + Optional<ItemCart> optionalItemCart = cart.getItems().stream() .filter(item -> item.getId().equals(itemCartId)) .findFirst(); if (optionalItemCart.isPresent()) { ItemCart itemCart = optionalItemCart.get(); - String Message; + String message; + if (quantity > 0) { - if (quantity > itemCart.getQuantity()) { - ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO(itemCart.getProductId(), quantity, ItemSource.WEB, itemCart.getProductType()); - ProductStockDTO stockResult = productStockService.checkStockWeb(productStockRequestDTO); + if (itemCart.getProductType() == ProductType.GIFT_CARD) { + long currentQuantity = itemCart.getQuantity(); + if (quantity > currentQuantity) { + long cardsToCreate = quantity - currentQuantity; + List<GiftCardDTO> newGiftCards = new ArrayList<>(); + + Long existingGiftCardId = itemCart.getGiftCardIds().get(0); + + for (int i = 0; i < cardsToCreate; i++) { + try { + String duplicationUrl = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.duplicateGiftCard(existingGiftCardId); + + logger.info("Duplication de la carte cadeau avec l'id : {}", existingGiftCardId); + + ResponseEntity<GiftCardDTO> response = mcRestTemplateService.postForObject( + duplicationUrl, null, GiftCardDTO.class); + + if (response.getStatusCode() == HttpStatus.CREATED) { + GiftCardDTO duplicatedGiftCard = response.getBody(); + assert duplicatedGiftCard != null; + itemCart.getGiftCardIds().add(duplicatedGiftCard.getGiftcardId()); + newGiftCards.add(duplicatedGiftCard); + } else { + cart.getInfos().add("Échec de la duplication de la carte cadeau supplémentaire numéro " + (i + 1)); + } + } catch (Exception e) { + cart.getInfos().add("Erreur lors de la duplication de la carte cadeau supplémentaire numéro " + (i + 1) + " : " + e.getMessage()); + } + } - if (!stockResult.isAvailable()) { - Message = "Le produit" + " " + stockResult.getProductDTO().getName() + " " + "n'est pas disponible en quantité suffisante."; + itemCart.setQuantity(quantity); - } else { - Message = "Le produit" + " " + stockResult.getProductDTO().getName() + " " + " a été modifié avec succès"; + double totalSolde = newGiftCards.stream() + .mapToDouble(GiftCardDTO::getSolde) + .sum(); + itemCart.setTotalPrice(totalSolde); + + message = "La quantité de cartes cadeaux a été augmentée à " + quantity; + } else if (quantity < currentQuantity) { + long cardsToRemove = currentQuantity - quantity; + for (int i = 0; i < cardsToRemove; i++) { + if (!itemCart.getGiftCardIds().isEmpty()) { + Long removedCardId = itemCart.getGiftCardIds().remove(0); + + try { + String deleteUrl = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.deleteGiftCard(removedCardId); + logger.info("Suppression de la carte cadeau avec l'id : {}", removedCardId); + + mcRestTemplateService.postForObject(deleteUrl, null, GiftCardDTO.class); + + } catch (Exception e) { + cart.getInfos().add("Erreur lors de la suppression de la carte cadeau avec l'ID " + removedCardId + " : " + e.getMessage()); + } + } + } itemCart.setQuantity(quantity); + + double totalSolde = itemCart.getGiftCardIds().size() * /* Solde par carte cadeau */ 0.0; // Remplacez 0.0 par le solde réel si disponible + itemCart.setTotalPrice(totalSolde); + message = "La quantité de cartes cadeaux a été réduite à " + quantity; + } else { + message = "La quantité de cartes cadeaux est déjà " + quantity; } } else { - Message = "Le produit" + " " + itemCart.getProduct().getName() + " " + " a été modifié avec succès"; - itemCart.setQuantity(quantity); + // Gestion des autres produits + if (quantity > itemCart.getQuantity()) { + ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO(itemCart.getProductId(), quantity, ItemSource.WEB, itemCart.getProductType()); + ProductStockDTO stockResult = productStockService.checkStockWeb(productStockRequestDTO); + + if (!stockResult.isAvailable()) { + message = "Le produit " + stockResult.getProductDTO().getName() + " n'est pas disponible en quantité suffisante."; + } else { + message = "Le produit " + stockResult.getProductDTO().getName() + " a été modifié avec succès"; + itemCart.setQuantity(quantity); + } + } else { + message = "Le produit " + itemCart.getProduct().getName() + " a été modifié avec succès"; + itemCart.setQuantity(quantity); + } } - cart.getInfos().add(Message); + cart.getInfos().add(message); } + } - cartService.applyPromoCodes(cart); - double total = cartService.calculateCartTotalPrice(cart); - cart.setTotalPrice(total); - return cartMapper.toDTO(cart); + + return cartService.reload(cart); } @Override - public CartDTO deleteItemCartById(Long clientId, Long itemCartId) { + public CartDTO deleteItemCartById(Long clientId, Long itemCartId) throws TechnicalException { ItemCart itemCart = itemCartRepository.findById(itemCartId) .orElseThrow(() -> new ItemNotFoundException("ItemCart not found with ID: " + itemCartId)); Cart cart = itemCart.getCart(); deleteItemCartById(cart, itemCartId); - cartMapper.removeInvalidPromoCodes(cart); cartRepository.save(cart); - return cartMapper.toDTO(cart); + + return cartService.reload(cart); } + @Override - public CartDTO deleteItemCartById(Cart cart, Long itemCartId) { + public CartDTO deleteItemCartById(Cart cart, Long itemCartId) throws TechnicalException { if (cart == null) { return null; } @@ -214,43 +379,58 @@ public class ItemCartServiceImp implements ItemCartService { .findFirst(); if (optionalItemCart.isPresent()) { ItemCart itemCart = optionalItemCart.get(); - ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO( - itemCart.getProductId(), - itemCart.getQuantity(), - itemCart.getSource(), - itemCart.getProductType() - ); - if (itemCart.getSource() == ItemSource.LIVE) { - productStockService.handleIncrementStock(productStockRequestDTO); + + if (itemCart.getProductType() == ProductType.GIFT_CARD) { + List<Long> giftCardIds = itemCart.getGiftCardIds(); + try { + String deleteUrl = nameUrl.getAddonsService() + NameUri.ADDONS + AddonsServicePath.DELETE_BATCH_GIFT_CARD; + HttpEntity<List<Long>> requestEntity = new HttpEntity<>(giftCardIds); + ResponseEntity<Void> response = mcRestTemplateService.postForObject(deleteUrl, requestEntity, Void.class); + if (response.getStatusCode() != HttpStatus.NO_CONTENT) { + cart.getInfos().add("Erreur lors de la suppression groupée des cartes cadeaux."); + } + } catch (Exception e) { + cart.getInfos().add("Erreur lors de la suppression groupée des cartes cadeaux : " + e.getMessage()); + } + } else { + ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO( + itemCart.getProductId(), + itemCart.getQuantity(), + itemCart.getSource(), + itemCart.getProductType() + ); + if (itemCart.getSource() == ItemSource.LIVE) { + productStockService.handleIncrementStock(productStockRequestDTO); + } + + ProductStockDTO productStockDTO = productStockService.getProduct(productStockRequestDTO); + cart.getInfos().add("Le produit " + productStockDTO.getProductDTO().getName() + " a été supprimé du panier."); } - ProductStockDTO productStockDTO = productStockService.getProduct(productStockRequestDTO); - cart.getInfos().add("Le produit" + " " + productStockDTO.getProductDTO().getName() + " a été supprimé du panier."); itemCart.setStatus(ItemCartStatus.DELETED_SITE); cart.setItemsCount(cart.getItemsCount() - 1); } cart = cartService.updateCartWithStockAvailability(cart); - cartService.applyPromoCodes(cart); - return cartMapper.toDTO(cart); + + return cartService.reload(cart); } @Override - public CartDTO deleteItemCartByIdFromBO(Long clientId, Long itemCartId) { + public CartDTO deleteItemCartByIdFromBO(Long clientId, Long itemCartId) throws TechnicalException { ItemCart itemCart = itemCartRepository.findById(itemCartId) .orElseThrow(() -> new ItemNotFoundException("ItemCart not found with ID: " + itemCartId)); Cart cart = itemCart.getCart(); deleteItemCartByIdFromBO(cart, itemCartId); - cartMapper.removeInvalidPromoCodes(cart); cartRepository.save(cart); - return cartMapper.toDTO(cart); + return cartService.reload(cart); } @Override - public CartDTO deleteItemCartByIdFromBO(Cart cart, Long itemCartId) { + public CartDTO deleteItemCartByIdFromBO(Cart cart, Long itemCartId) throws TechnicalException { if (cart == null) { return null; } @@ -281,8 +461,8 @@ public class ItemCartServiceImp implements ItemCartService { } cart = cartService.updateCartWithStockAvailability(cart); - cartService.applyPromoCodes(cart); - return cartMapper.toDTO(cart); + + return cartService.reload(cart); } diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/ProductStockServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/ProductStockServiceImp.java index 2bc7b6588ce5057ad2327cb7e55dc3a2f897cb37..fab5b77258d0d933480bf6570935fb3c2782e63a 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/ProductStockServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/ProductStockServiceImp.java @@ -26,7 +26,7 @@ import java.util.List; public class ProductStockServiceImp implements ProductStockService { private final MCRestTemplateService mcRestTemplateService; - private static final Logger logger = LoggerFactory.getLogger(CodePromoClientServiceImp.class); + private static final Logger logger = LoggerFactory.getLogger(ProductStockServiceImp.class); private final NameUrl nameUrl; public ProductStockServiceImp(MCRestTemplateService mcRestTemplateService, NameUrl nameUrl) { diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/PromoCodeUtilServiceImpl.java b/src/main/java/com/marketingconfort/mydressin/services/impl/PromoCodeUtilServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..588c6203f41ba41b89d934918a700336ceae7617 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/PromoCodeUtilServiceImpl.java @@ -0,0 +1,344 @@ +package com.marketingconfort.mydressin.services.impl; + +import com.marketingconfort.mydressin.common.Addons.dtos.PromoCodeDTO; +import com.marketingconfort.mydressin.common.Addons.enumerations.PromoCodeType; +import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; +import com.marketingconfort.mydressin.dtos.CartDTO; +import com.marketingconfort.mydressin.dtos.ItemCartDTO; +import com.marketingconfort.mydressin.services.ExternalApiService; +import com.marketingconfort.mydressin.services.PromoCodeUtilService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Service +public class PromoCodeUtilServiceImpl implements PromoCodeUtilService { + + private static final Logger logger = LoggerFactory.getLogger(PromoCodeUtilServiceImpl.class); + + private final ExternalApiService externalApiService; + + public PromoCodeUtilServiceImpl(ExternalApiService externalApiService) { + this.externalApiService = externalApiService; + } + + + @Override + public CartDTO calculatePromoCodeDiscountsToCart(CartDTO cartDTO) throws TechnicalException { + if (cartDTO == null) { + logger.warn("cartDTO is null."); + return cartDTO; + } + + if (cartDTO.getPromoCodeIds() == null || cartDTO.getPromoCodeIds().isEmpty()) { + logger.warn("No promo codes provided in the cartDTO with client ID: {}.", cartDTO.getClientId()); + return cartDTO; + } + + if (cartDTO.getItems().isEmpty()) { + logger.warn("Cart has no items. Skipping promo code calculation."); + return cartDTO; + } + + boolean containsOnlyGiftCards = cartDTO.getItems().stream() + .allMatch(item -> item.getProductType() == ProductType.GIFT_CARD); + + if (containsOnlyGiftCards) { + logger.info("Cart contains only Gift Cards. No promo code will be applied."); + return cartDTO; + } + + List<PromoCodeDTO> promoCodeDTOList = externalApiService.getPromoCodesByIds(cartDTO.getPromoCodeIds()); + + double finalTotalCartDiscount = 0.0; + + for (PromoCodeDTO promoCodeDTO : promoCodeDTOList) { + long acceptedItemsCount = cartDTO.getItems().stream() + .filter(item -> isItemIncludedOrExcluded(item, promoCodeDTO)) + .count(); + + double promoCodeTotalCartDiscount = 0.0; + + switch (promoCodeDTO.getPromoCodeType()) { + case DISCOUNT_PER_CART -> { + promoCodeTotalCartDiscount = applyDiscountPerCart(promoCodeDTO, cartDTO); + if (promoCodeTotalCartDiscount > 0) { + for (ItemCartDTO item : cartDTO.getItems()) { + if (item.getProductType() != ProductType.GIFT_CARD) { + double itemDiscount = promoCodeTotalCartDiscount / acceptedItemsCount; + if (itemDiscount > 0) { + double newTotalPrice = item.getTotalPrice() - itemDiscount; + item.setTotalPrice(Math.max(newTotalPrice, 0.0)); + item.setPromoCodeDiscounts(item.getPromoCodeDiscounts() + itemDiscount); + } + } + } + } + } + case PERCENTAGE_PER_CART -> { + promoCodeTotalCartDiscount = applyPercentagePerCart(promoCodeDTO, cartDTO); + if (promoCodeTotalCartDiscount > 0) { + for (ItemCartDTO item : cartDTO.getItems()) { + if (item.getProductType() != ProductType.GIFT_CARD) { + double itemDiscount = promoCodeTotalCartDiscount / acceptedItemsCount; + if (itemDiscount > 0) { + double newTotalPrice = item.getTotalPrice() - itemDiscount; + item.setTotalPrice(Math.max(newTotalPrice, 0.0)); + item.setPromoCodeDiscounts(item.getPromoCodeDiscounts() + itemDiscount); + } + } + } + } + } + case DISCOUNT_PER_PRODUCT -> { + for (ItemCartDTO item : cartDTO.getItems()) { + double itemDiscount = applyDiscountPerProduct(promoCodeDTO, item); + if (itemDiscount > 0) { + double newTotalPrice = item.getTotalPrice() - itemDiscount; + item.setTotalPrice(Math.max(newTotalPrice, 0.0)); + item.setPromoCodeDiscounts(item.getPromoCodeDiscounts() + itemDiscount); + promoCodeTotalCartDiscount += itemDiscount; + } + } + } + default -> { + logger.warn("Unsupported promo code type: {}", promoCodeDTO.getPromoCodeType()); + cartDTO.getErrorsMessages().add("Type de code promo non supporté : " + promoCodeDTO.getCode()); + continue; + } + } + + if (promoCodeTotalCartDiscount > 0) { + finalTotalCartDiscount += promoCodeTotalCartDiscount; + cartDTO.getInfos().add("Réduction appliquée avec le code promo " + promoCodeDTO.getCode() + " : " + promoCodeTotalCartDiscount + "€ sur le prix total du panier."); + } else { + cartDTO.getErrorsMessages().add("Aucune réduction applicable avec le code promo : " + promoCodeDTO.getCode()); + } + } + + double updatedTotalPrice = cartDTO.getTotalPrice() - finalTotalCartDiscount; + cartDTO.setTotalPrice(Math.max(updatedTotalPrice, 0.0)); + cartDTO.setPromoCodeDiscounts(finalTotalCartDiscount); + + logger.info("Total discount applied: {}. Updated cart total: {}", finalTotalCartDiscount, cartDTO.getTotalPrice()); + return cartDTO; + } + + @Override + public boolean validateCartItemsForPromoCode(CartDTO cartDTO, PromoCodeDTO promoCodeDTO) { + long acceptedItemsCount = cartDTO.getItems().stream() + .filter(item -> isItemIncludedOrExcluded(item, promoCodeDTO)) + .count(); + + if (promoCodeDTO.getPromoCodeType() == PromoCodeType.DISCOUNT_PER_CART || + promoCodeDTO.getPromoCodeType() == PromoCodeType.PERCENTAGE_PER_CART) { + if (acceptedItemsCount != cartDTO.getItems().size()) { + logger.info("Not all items in the cart are eligible for the promo code."); + return false; + } + } else if (promoCodeDTO.getPromoCodeType() == PromoCodeType.DISCOUNT_PER_PRODUCT) { + if (acceptedItemsCount == 0) { + logger.info("No eligible items found in the cart for the promo code."); + return false; + } + } + + logger.info("Promo code is applicable to the cart."); + return true; + } + + @Override + public boolean isItemIncludedOrExcluded(ItemCartDTO item, PromoCodeDTO promoCodeDTO) { + if (item.getProductType() == ProductType.GIFT_CARD) { + logger.info("Item with ID {} is a gift card and excluded from promo code.", item.getProductId()); + return false; + } + + boolean isExcluded = false; + boolean isIncluded = false; + + if (item.getProductType() == ProductType.SIMPLE) { + isExcluded = promoCodeDTO.getProductsExcludedIds() != null && + promoCodeDTO.getProductsExcludedIds().contains(item.getProductId()); + } else if (item.getProductType() == ProductType.VARIABLE) { + isExcluded = promoCodeDTO.getVariationsExcludedIds() != null && + promoCodeDTO.getVariationsExcludedIds().contains(item.getProductId()); + } + + if (!isExcluded && promoCodeDTO.getCategoriesExcludedIds() != null) { + isExcluded = promoCodeDTO.getCategoriesExcludedIds() != null && + promoCodeDTO.getCategoriesExcludedIds().stream() + .anyMatch(categoryId -> item.getProduct().getCategoriesIds().contains(categoryId)); + } + + if (isExcluded) { + logger.info("Item with ID {} is excluded from the promo code.", item.getProductId()); + return false; + } + + if ( promoCodeDTO.getProductsIncludedIds() != null && promoCodeDTO.getProductsIncludedIds().isEmpty() && + promoCodeDTO.getVariationsIncludedIds() != null && promoCodeDTO.getVariationsIncludedIds().isEmpty() && + promoCodeDTO.getCategoriesIncludedIds() != null && promoCodeDTO.getCategoriesIncludedIds().isEmpty()) { + isIncluded = true; + } else { + if (item.getProductType() == ProductType.SIMPLE) { + isIncluded = promoCodeDTO.getProductsIncludedIds() != null && + promoCodeDTO.getProductsIncludedIds().contains(item.getProductId()); + } else if (item.getProductType() == ProductType.VARIABLE) { + isIncluded = promoCodeDTO.getVariationsIncludedIds() != null && + promoCodeDTO.getVariationsIncludedIds().contains(item.getProductId()); + } + + boolean isCategoryIncluded = promoCodeDTO.getCategoriesIncludedIds() != null && + promoCodeDTO.getCategoriesIncludedIds().stream() + .anyMatch(categoryId -> item.getProduct().getCategoriesIds() != null && + item.getProduct().getCategoriesIds().contains(categoryId)); + + boolean isCategoryExcluded = promoCodeDTO.getCategoriesExcludedIds() != null && + promoCodeDTO.getCategoriesExcludedIds().stream() + .anyMatch(categoryId -> item.getProduct().getCategoriesIds() != null && + item.getProduct().getCategoriesIds().contains(categoryId)); + + if (isIncluded && isCategoryExcluded) { + logger.info("Item with ID {} is excluded due to its category being excluded.", item.getProductId()); + return false; + } + + if (!isIncluded && isCategoryIncluded) { + logger.info("Item with ID {} is excluded due to the product being explicitly excluded.", item.getProductId()); + return false; + } + + isIncluded = isIncluded || isCategoryIncluded; + + if (!isIncluded) { + logger.info("Item with ID {} is not included for the promo code. Product included: {}, Category included: {}", + item.getProductId(), isIncluded, isCategoryIncluded); + return false; + } + } + + if (isIncluded) { + logger.info("Item with ID {} is included for the promo code.", item.getProductId()); + return true; + } + + logger.info("Item with ID {} is not included for the promo code.", item.getProductId()); + return false; + } + + @Override + public boolean isPromoCodeReducingCartTotal(CartDTO cart, PromoCodeDTO promoCodeDTO) { + if (promoCodeDTO == null) { + logger.warn("PromoCodeDTO is null. Validation failed."); + return false; + } + if (cart == null || cart.getItems() == null || cart.getItems().isEmpty()) { + logger.warn("Cart is null or empty. Validation failed."); + return false; + } + + double totalPriceBeforeDiscount = cart.getTotalPrice(); + + if (totalPriceBeforeDiscount <= 0) { + logger.warn("Cart total price is zero or negative. Promo code cannot reduce total."); + return false; + } + + double totalPriceAfterDiscount = switch (promoCodeDTO.getPromoCodeType()) { + case DISCOUNT_PER_CART -> totalPriceBeforeDiscount - applyDiscountPerCart(promoCodeDTO, cart); + case PERCENTAGE_PER_CART -> totalPriceBeforeDiscount - applyPercentagePerCart(promoCodeDTO, cart); + case DISCOUNT_PER_PRODUCT -> calculateTotalAfterProductDiscount(promoCodeDTO, cart); + default -> { + logger.warn("Unsupported promo code type: {}", promoCodeDTO.getPromoCodeType()); + yield totalPriceBeforeDiscount; + } + }; + + if (totalPriceAfterDiscount >= totalPriceBeforeDiscount) { + logger.info("Promo code does not reduce the total. Before: {}, After: {}", totalPriceBeforeDiscount, totalPriceAfterDiscount); + return false; + } + + totalPriceAfterDiscount = Math.max(totalPriceAfterDiscount, 0); + + logger.info("Promo code validation successful: Before Discount = {}, After Discount = {}, Reduction = {}", + totalPriceBeforeDiscount, totalPriceAfterDiscount, totalPriceBeforeDiscount - totalPriceAfterDiscount); + + return true; + } + + private double calculateTotalAfterProductDiscount(PromoCodeDTO promoCodeDTO, CartDTO cart) { + return cart.getItems().stream() + .mapToDouble(item -> { + double discount = applyDiscountPerProduct(promoCodeDTO, item); + return Math.max(item.getTotalPrice() - discount, 0); + }) + .sum(); + } + + @Override + public double applyDiscountPerProduct(PromoCodeDTO promoCodeDTO, ItemCartDTO item) { + if (item.getProductType() == ProductType.GIFT_CARD) { + logger.info("Item with ID {} is a gift card and excluded from discount.", item.getProductId()); + return 0.0; + } + + if (!isItemIncludedOrExcluded(item, promoCodeDTO)) { + logger.info("Item with ID {} is not eligible for discount.", item.getProductId()); + return 0.0; + } + + if (promoCodeDTO.getMinAmount().compareTo(BigDecimal.valueOf(item.getTotalPrice())) > 0) { + logger.info("Item with ID {} does not meet the minimum required amount for discount.", item.getProductId()); + return 0.0; + } + + double discount = promoCodeDTO.getValueCode().doubleValue(); + double finalPrice = item.getTotalPrice() - discount; + + if (finalPrice < 0) { + logger.info("Discount exceeds product price for item ID {}. No discount applied.", item.getProductId()); + return 0.0; + } + + logger.info("Discount applied per product (Product/Variation ID {}): {}", item.getProductId(), discount); + return discount; + } + + @Override + public double applyDiscountPerCart(PromoCodeDTO promoCodeDTO, CartDTO cart) { + if (cart.getItems().stream().anyMatch(item -> item.getProductType() == ProductType.GIFT_CARD)) { + logger.info("Cart contains gift cards, which are excluded from discount."); + return 0.0; + } + + if (promoCodeDTO.getMinAmount().compareTo(BigDecimal.valueOf(cart.getTotalPrice()).setScale(2, RoundingMode.HALF_UP)) > 0) { + logger.info("Cart total does not meet the minimum required amount for discount."); + return 0.0; + } + + double discount = promoCodeDTO.getValueCode().doubleValue(); + logger.info("Discount applied is: {}, per cart client ID: {}", discount, cart.getClientId()); + return discount; + } + + @Override + public double applyPercentagePerCart(PromoCodeDTO promoCodeDTO, CartDTO cart) { + if (promoCodeDTO.getMinAmount().compareTo(BigDecimal.valueOf(cart.getTotalPrice())) > 0) { + logger.info("Cart total does not meet the minimum required amount for percentage discount."); + return 0.0; + } + + double percentage = promoCodeDTO.getValueCode().doubleValue(); + double discount = (cart.getTotalPrice() * percentage) / 100; + logger.info("Percentage discount applied per cart: {}% , per cart client ID: {}", percentage, cart.getClientId()); + return discount; + } + +} diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java index dd537ccb2aa222002d981b8ec24738a013cbbb4d..4d58026bf9baf4bed596e9d450635fb32180586a 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java @@ -13,7 +13,7 @@ import com.marketingconfort.mydressin.dtos.ItemRequestDTO; import com.marketingconfort.mydressin.exceptions.ProductOutOfStockException; import com.marketingconfort.mydressin.exceptions.SaleSessionExpiredException; import com.marketingconfort.mydressin.exceptions.SaleSessionNotFoundException; -import com.marketingconfort.mydressin.mappers.OrderMapper; +import com.marketingconfort.mydressin.mappers.SessionOrderMapper; import com.marketingconfort.mydressin.mappers.SaleSessionMapper; import com.marketingconfort.mydressin.repositories.SaleSessionRepository; import com.marketingconfort.mydressin.repositories.SessionOrderRepository; @@ -40,7 +40,7 @@ public class SaleSessionServiceImp implements SaleSessionService { private final CartService cartService; private final ProductStockService productStockService; private final SessionOrderRepository orderRepository; - private final OrderMapper orderMapper; + private final SessionOrderMapper sessionOrderMapper; private final UnregisteredCartService unregisteredCartService; private final SessionOrderRepository sessionOrderRepository; @@ -162,7 +162,7 @@ public class SaleSessionServiceImp implements SaleSessionService { } orderRepository.save(order); - return orderMapper.toDTO(order); + return sessionOrderMapper.toDTO(order); } diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/SessionOrderServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/SessionOrderServiceImp.java index 4d8b985457ef2e824c3d13a3eeb72a397a77bda4..dbab8df69006851fa91eaf7d0f2adc946bff283f 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/SessionOrderServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/SessionOrderServiceImp.java @@ -15,7 +15,7 @@ import com.marketingconfort.mydressin.exceptions.InvalidQuantityException; import com.marketingconfort.mydressin.exceptions.OrderNotFoundException; import com.marketingconfort.mydressin.exceptions.ProductOutOfStockException; import com.marketingconfort.mydressin.exceptions.SaleSessionExpiredException; -import com.marketingconfort.mydressin.mappers.OrderMapper; +import com.marketingconfort.mydressin.mappers.SessionOrderMapper; import com.marketingconfort.mydressin.repositories.SessionOrderRepository; import com.marketingconfort.mydressin.services.ClientService; import com.marketingconfort.mydressin.services.ProductStockService; @@ -34,7 +34,7 @@ public class SessionOrderServiceImp implements SessionOrderService { private SessionOrderRepository sessionOrderRepository; private ProductStockService productStockService; private ClientService clientService; - private OrderMapper sessionOrderMapper; + private SessionOrderMapper sessionOrderMapper; @Override diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/StatisticsServiceImpl.java b/src/main/java/com/marketingconfort/mydressin/services/impl/StatisticsServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..67aee69f537d39aa88c69d0ee84bdb4d2d3bc0bc --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/StatisticsServiceImpl.java @@ -0,0 +1,295 @@ +package com.marketingconfort.mydressin.services.impl; +import com.marketingconfort.mydressin.common.cart.dtos.GiftCardDTO; +import com.marketingconfort.mydressin.common.cart.dtos.ProductCartDTO; +import com.marketingconfort.mydressin.common.cart.dtos.ProductStockDTO; +import com.marketingconfort.mydressin.common.cart.dtos.ProductStockRequestDTO; +import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; +import com.marketingconfort.mydressin.common.cart.models.Cart; +import com.marketingconfort.mydressin.common.cart.models.ItemCart; +import com.marketingconfort.mydressin.common.stockmanagement.enumurations.ProductType; +import com.marketingconfort.mydressin.dtos.StatisticsDTOs.*; +import com.marketingconfort.mydressin.repositories.CartRepository; +import com.marketingconfort.mydressin.repositories.ItemCartRepository; +import com.marketingconfort.mydressin.services.ProductStockService; +import com.marketingconfort.mydressin.services.StatisticsService; +import com.marketingconfort.starter.core.exceptions.TechnicalException; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.JoinType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.jpa.domain.Specification; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + + +@Service +public class StatisticsServiceImpl implements StatisticsService { + + private static final Logger logger = LoggerFactory.getLogger(StatisticsServiceImpl.class); + + private final ItemCartRepository itemCartRepository; + private final ProductStockService productStockService; + private final ExternalApiServiceImpl externalApiService; + private final CartRepository cartRepository; + + public StatisticsServiceImpl(ItemCartRepository itemCartRepository, ProductStockService productStockService, ExternalApiServiceImpl externalApiService, CartRepository cartRepository) { + this.itemCartRepository = itemCartRepository; + this.productStockService = productStockService; + this.externalApiService = externalApiService; + this.cartRepository = cartRepository; + } + @Override + public double calculateTheoreticalRevenue(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate) throws TechnicalException { + Specification<ItemCart> spec = Specification.where(null); + + if (sources != null && !sources.isEmpty()) { + spec = spec.and((root, query, criteriaBuilder) -> root.get("source").in(sources)); + } + + if (startDate != null) { + spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.greaterThanOrEqualTo(root.get("addedDate"), startDate)); + } + + if (endDate != null) { + spec = spec.and((root, query, criteriaBuilder) -> criteriaBuilder.lessThanOrEqualTo(root.get("addedDate"), endDate)); + } + + List<ItemCart> itemCarts = itemCartRepository.findAll(spec); + + logger.info("Number of ItemCarts retrieved: {}", itemCarts.size()); + + double totalRevenue = 0.0; + + for (ItemCart itemCart : itemCarts) { + logger.info("Processing ItemCart with ID: {}", itemCart.getId()); + double unitValue = getUnitPriceForItemCart(itemCart); + logger.info("Unit value for item ID {}: {}", itemCart.getId(), unitValue); + totalRevenue += itemCart.getQuantity() * unitValue; + } + + logger.info("Total theoretical revenue calculated: {}", totalRevenue); + + return totalRevenue; + } + + + private double getUnitPriceForItemCart(ItemCart itemCart) throws TechnicalException { + logger.info("Getting unit price for ItemCart ID: {}", itemCart.getId()); + + ProductType productType = itemCart.getProductType(); + + if (productType == ProductType.GIFT_CARD) { + logger.info("ItemCart ID: {} is a GIFT_CARD. Retrieving gift card details.", itemCart.getId()); + GiftCardDTO giftCard = externalApiService.getGiftCardById(itemCart.getProductId()); + if (giftCard != null) { + double initialBalance = giftCard.getInitialBalance(); + logger.info("Initial balance for GiftCard ID {}: {}", itemCart.getProductId(), initialBalance); + return initialBalance; + } else { + logger.warn("GiftCard not found for ID: {}. Ignoring ItemCart ID: {}", itemCart.getProductId(), itemCart.getId()); + return 0.0; + } + } else if (productType == ProductType.SIMPLE || productType == ProductType.VARIABLE) { + ProductStockRequestDTO productStockRequestDTO = new ProductStockRequestDTO( + itemCart.getProductId(), + itemCart.getQuantity(), + itemCart.getSource(), + itemCart.getProductType() + ); + + logger.info("Requesting product stock for product ID: {}", itemCart.getProductId()); + + try { + ProductStockDTO productStockDTO = productStockService.getProduct(productStockRequestDTO); + + if (productStockDTO != null && productStockDTO.getProductDTO() != null) { + ProductCartDTO product = productStockDTO.getProductDTO(); + double price = (product.getPromoPrice() > 0) ? product.getPromoPrice() : product.getRegularPrice(); + logger.info("Price found for product ID {}: {}", itemCart.getProductId(), price); + return price; + } else { + logger.warn("Product not found for ID: {}. Ignoring ItemCart ID: {}", itemCart.getProductId(), itemCart.getId()); + return 0.0; + } + } catch (HttpClientErrorException e) { + if (e.getStatusCode() == HttpStatus.NOT_FOUND) { + logger.warn("Product not found with ID: {}. Ignoring ItemCart ID: {}", itemCart.getProductId(), itemCart.getId()); + return 0.0; + } else { + logger.error("HTTP error while fetching product with ID: {}. Status Code: {}, Error: {}", itemCart.getProductId(), e.getStatusCode(), e.getMessage()); + return 0.0; + } + } catch (Exception e) { + logger.error("Exception while fetching product with ID: {}. Error: {}", itemCart.getProductId(), e.getMessage()); + return 0.0; + } + } else { + logger.warn("Unsupported ProductType: {}. Ignoring ItemCart ID: {}", productType, itemCart.getId()); + return 0.0; + } + } + + + @Override + public ChartDataDTO getAddToCartTrends(List<ItemSource> sources) throws TechnicalException { + List<String> sourceStrings = null; + if (sources != null && !sources.isEmpty()) { + sourceStrings = sources.stream() + .map(ItemSource::name) + .collect(Collectors.toList()); + } + + List<DateCountDTO> currentMonthData = getAddToCartDataForCurrentMonth(sourceStrings); + List<DateCountDTO> previousMonthData = getAddToCartDataForPreviousMonth(sourceStrings); + List<DateCountDTO> previousYearData = getAddToCartDataForPreviousYear(sourceStrings); + + Set<String> allDates = new TreeSet<>(); + currentMonthData.forEach(d -> allDates.add(d.getDate().toString())); + previousMonthData.forEach(d -> allDates.add(d.getDate().toString())); + previousYearData.forEach(d -> allDates.add(d.getDate().toString())); + + List<String> labels = new ArrayList<>(allDates); + + SeriesDataDTO currentMonthSeries = createSeriesData("Mois en cours", "line", "solid", labels, currentMonthData); + SeriesDataDTO previousMonthSeries = createSeriesData("Période précédente", "line", "solid", labels, previousMonthData); + SeriesDataDTO previousYearSeries = createSeriesData("Année précédente", "line", "solid", labels, previousYearData); + + ChartDataDTO chartData = new ChartDataDTO(); + chartData.setLabels(labels); + chartData.setSeries(Arrays.asList(previousMonthSeries, previousYearSeries, currentMonthSeries)); + + return chartData; + } + + private List<DateCountDTO> getAddToCartDataForCurrentMonth(List<String> sources) { + LocalDateTime startDate = LocalDate.now().withDayOfMonth(1).atStartOfDay(); + LocalDateTime endDate = LocalDateTime.now(); + + List<Object[]> results = itemCartRepository.countAddToCartByDateNative( + sources != null && !sources.isEmpty() ? String.valueOf(sources) : null, + startDate, + endDate + ); + + return mapNativeResultToDateCountDTO(results); + } + + private List<DateCountDTO> getAddToCartDataForPreviousMonth(List<String> sources) { + LocalDate previousMonth = LocalDate.now().minusMonths(1); + LocalDateTime startDate = previousMonth.withDayOfMonth(1).atStartOfDay(); + LocalDateTime endDate = previousMonth.withDayOfMonth(previousMonth.lengthOfMonth()).atTime(23, 59, 59); + + List<Object[]> results = itemCartRepository.countAddToCartByDateNative( + sources != null && !sources.isEmpty() ? String.valueOf(sources) : null, + startDate, + endDate + ); + + return mapNativeResultToDateCountDTO(results); + } + + private List<DateCountDTO> getAddToCartDataForPreviousYear(List<String> sources) { + LocalDate previousYear = LocalDate.now().minusYears(1); + LocalDateTime startDate = previousYear.withDayOfYear(1).atStartOfDay(); + LocalDateTime endDate = previousYear.withDayOfYear(previousYear.lengthOfYear()).atTime(23, 59, 59); + + List<Object[]> results = itemCartRepository.countAddToCartByDateNative( + sources != null && !sources.isEmpty() ? String.valueOf(sources) : null, + startDate, + endDate + ); + + return mapNativeResultToDateCountDTO(results); + } + + private List<DateCountDTO> mapNativeResultToDateCountDTO(List<Object[]> results) { + List<DateCountDTO> dateCounts = new ArrayList<>(); + for (Object[] row : results) { + LocalDate date = ((java.sql.Date) row[0]).toLocalDate(); + Long count = ((Number) row[1]).longValue(); + dateCounts.add(new DateCountDTO(date, count)); + } + return dateCounts; + } + + private SeriesDataDTO createSeriesData(String name, String type, String fill, List<String> labels, List<DateCountDTO> data) { + Map<String, Long> dataMap = data.stream() + .collect(Collectors.toMap(d -> d.getDate().toString(), DateCountDTO::getCount)); + + List<Long> seriesData = labels.stream() + .map(date -> dataMap.getOrDefault(date, 0L)) + .collect(Collectors.toList()); + + return new SeriesDataDTO(name, type, fill, seriesData); + } + + + + @Override + public Long getTotalCarts(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate) { + Specification<Cart> spec = Specification.where(null); + + if (sources != null && !sources.isEmpty()) { + spec = spec.and((root, query, criteriaBuilder) -> { + Join<Cart, ItemCart> itemsJoin = root.join("items", JoinType.INNER); + return itemsJoin.get("source").in(sources); + }); + } + + if (startDate != null) { + spec = spec.and((root, query, criteriaBuilder) -> + criteriaBuilder.greaterThanOrEqualTo(root.get("createdDate"), startDate)); + } + + if (endDate != null) { + spec = spec.and((root, query, criteriaBuilder) -> + criteriaBuilder.lessThanOrEqualTo(root.get("createdDate"), endDate)); + } + + return cartRepository.count(spec); + } + + @Override + public List<DailyAmountDTO> calculateTheoreticalAmounts(List<ItemSource> sources, LocalDateTime startDate, LocalDateTime endDate) { + List<String> sourcesStr; + if (sources != null && !sources.isEmpty()) { + sourcesStr = sources.stream().map(Enum::name).collect(Collectors.toList()); + } else { + sourcesStr = Arrays.asList(ItemSource.values()).stream().map(Enum::name).collect(Collectors.toList()); + } + + List<Object[]> results = cartRepository.sumCartsByDate(sourcesStr, startDate, endDate); + + List<DailyAmountDTO> dailyAmounts = new ArrayList<>(); + for (Object[] row : results) { + LocalDate date = ((java.sql.Date) row[0]).toLocalDate(); + Double amount = ((Number) row[1]).doubleValue(); + dailyAmounts.add(new DailyAmountDTO(date, amount)); + } + + return dailyAmounts; + } + + @Override + public TheoreticalAmountResponseDTO getTheoreticalAmountResponse(TheoreticalAmountRequestDTO requestDTO) { + List<DailyAmountDTO> dailyAmounts = calculateTheoreticalAmounts( + requestDTO.getSources(), + requestDTO.getStartDate(), + requestDTO.getEndDate() + ); + TheoreticalAmountResponseDTO responseDTO = new TheoreticalAmountResponseDTO(); + responseDTO.setDailyAmounts(dailyAmounts); + return responseDTO; + } + + + +} + + diff --git a/src/main/java/com/marketingconfort/mydressin/services/implementations/CartCountConfigServiceImpl.java b/src/main/java/com/marketingconfort/mydressin/services/implementations/CartCountConfigServiceImpl.java index d549b40d7a59942f8d8afccc644702ba1f0cc2e6..5a0fe533401b1a61359bb7fdc45cb1ae3c7c936b 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/implementations/CartCountConfigServiceImpl.java +++ b/src/main/java/com/marketingconfort/mydressin/services/implementations/CartCountConfigServiceImpl.java @@ -1,25 +1,19 @@ package com.marketingconfort.mydressin.services.implementations; -import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.models.CartCountConfig; -import com.marketingconfort.mydressin.common.cart.models.ItemCart; import com.marketingconfort.mydressin.dtos.CartCountConfigDTO; import com.marketingconfort.mydressin.mappers.CartCountConfigMapper; -import com.marketingconfort.mydressin.common.cart.models.Cart; import com.marketingconfort.mydressin.repositories.CartCountConfigRepository; -import com.marketingconfort.mydressin.repositories.CartRepository; import com.marketingconfort.mydressin.services.CartCountConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.Duration; -import java.util.List; + import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.UUID; -import java.time.LocalDateTime; @Service diff --git a/src/main/resources/application-rec.yml b/src/main/resources/application-rec.yml index a6028a2cec155f8fb250f5277f7c70c5efe4b6e3..8c7e6b207d156e37506abdd0a4f2ea86d01b9364 100644 --- a/src/main/resources/application-rec.yml +++ b/src/main/resources/application-rec.yml @@ -1,8 +1,13 @@ spring: datasource: - url: jdbc:postgresql://195.35.24.133:5432/mydressin-cart-service-local-db + url: jdbc:postgresql://92.113.29.13:5432/mydressin-cart-service-local-db username: mydressin-cart-service-local-user password: J8zgm427? + security: + oauth2: + resourceserver: + jwt: + issuer-uri: https://app1.mydressin-server.com:8444/auth/realms/mydressin autoconfigure: exclude: org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration