Skip to content

Commit

Permalink
story 5 - apply discount
Browse files Browse the repository at this point in the history
  • Loading branch information
waritthorn-v committed Mar 24, 2024
1 parent b69aa71 commit bc07125
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 18 deletions.
1 change: 1 addition & 0 deletions kbazaar/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ dependencies {

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.projectlombok:lombok:1.18.22'
implementation 'org.postgresql:postgresql:42.7.3'
Expand Down
8 changes: 3 additions & 5 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/Cart.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
import com.kampus.kbazaar.shopper.Shopper;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -25,14 +23,14 @@ public class Cart {
private Long id;

@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
private List<CartProduct> cartProducts;
private List<CartProduct> cartProducts = new ArrayList<>();

@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(
name = "cart_promotion",
joinColumns = @JoinColumn(name = "cart_id"),
inverseJoinColumns = @JoinColumn(name = "promotion_id"))
private Set<Promotion> promotions;
private Set<Promotion> promotions = new HashSet<>();

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "shopper_id", referencedColumnName = "id")
Expand Down
12 changes: 12 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartController.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.promotion.Promotion;
import com.kampus.kbazaar.promotion.PromotionService;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -11,6 +14,7 @@
@RequiredArgsConstructor
public class CartController {

private final PromotionService promotionService;
private final CartService cartService;

@Value("${enabled.shipping.fee:true}")
Expand Down Expand Up @@ -40,4 +44,12 @@ public CartResponse addProduct(

return cartResponse;
}

@PostMapping("/carts/{username}/promotions")
public CartResponse applyDiscount(
@PathVariable("username") String username, @RequestBody @Valid CartRequestDto req) {
Promotion promotion = promotionService.getPromotionsFromCode(req.getCode());
CartResponse result = this.cartService.applyDiscount(username, promotion);
return result;
}
}
10 changes: 10 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartRepository.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.shopper.Shopper;
import java.util.ArrayList;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface CartRepository extends JpaRepository<Cart, Long> {
Cart findByShopper(Shopper shopper);

Optional<Cart> findByShopperId(Long shopperId);

@Query("SELECT c.cartProducts FROM Cart c WHERE c.id = :cartId")
ArrayList<Product> findProductsByCartId(@Param("cartId") Long cartId);
}
10 changes: 10 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.kampus.kbazaar.cart;

import jakarta.validation.constraints.NotBlank;
import lombok.Data;

@Data
public class CartRequestDto {
@NotBlank(message = "is required.")
private String code;
}
17 changes: 17 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import com.kampus.kbazaar.exceptions.NotFoundException;
import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.product.ProductService;
import com.kampus.kbazaar.promotion.Promotion;
import com.kampus.kbazaar.shopper.ShopperService;
import java.util.ArrayList;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -36,4 +38,19 @@ public Cart addProductByUsernameAndProductSku(
new InternalServerException(
"Product out of stock or failed to add product to cart"));
}

public CartResponse applyDiscount(String username, Promotion promotion) {
Cart cart = findCartByUsername(username);
cart.addPromotion(promotion);
cartRepository.save(cart);
return cart.toCartResponse();
}

public Optional<Cart> findCartByShopperId(Long shopperId) {
return this.cartRepository.findByShopperId(shopperId);
}

public ArrayList<Product> getProductsByCartId(Long cartId) {
return cartRepository.findProductsByCartId(cartId);
}
}
109 changes: 109 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/OrderResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.product.Product;
import java.math.BigDecimal;
import java.util.ArrayList;

public class OrderResponse {
private String username;
private ArrayList<Product> items;
private BigDecimal totalPrice;
private BigDecimal totalDiscount;

public OrderResponse() {
this.items = new ArrayList<>();
}

// Getters and setters

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public ArrayList<Product> getItems() {
return items;
}

public void setItems(ArrayList<Product> items) {
this.items = items;
}

public BigDecimal getTotalPrice() {
return totalPrice;
}

public void setTotalPrice(BigDecimal totalPrice) {
this.totalPrice = totalPrice;
}

public BigDecimal getTotalDiscount() {
return totalDiscount;
}

public void setTotalDiscount(BigDecimal totalDiscount) {
this.totalDiscount = totalDiscount;
}

public static class Item {
private String sku;
private String name;
private int quantity;
private BigDecimal price;
private BigDecimal discount;
private BigDecimal finalPrice;

// Getters and setters

public String getSku() {
return sku;
}

public void setSku(String sku) {
this.sku = sku;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getQuantity() {
return quantity;
}

public void setQuantity(int quantity) {
this.quantity = quantity;
}

public BigDecimal getPrice() {
return price;
}

public void setPrice(BigDecimal price) {
this.price = price;
}

public BigDecimal getDiscount() {
return discount;
}

public void setDiscount(BigDecimal discount) {
this.discount = discount;
}

public BigDecimal getFinalPrice() {
return finalPrice;
}

public void setFinalPrice(BigDecimal finalPrice) {
this.finalPrice = finalPrice;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -61,7 +58,7 @@ public class Promotion {
@ManyToMany(
cascade = {CascadeType.ALL},
mappedBy = "promotions")
private Set<Cart> carts;
private Set<Cart> carts = new HashSet<>();

public PromotionResponse toResponse() {
return new PromotionResponse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@ public PromotionResponse getPromotionByCode(String code) {
.map(Promotion::toResponse)
.orElseThrow(() -> new NotFoundException("Promotion not found"));
}

public Promotion getPromotionsFromCode(String code) {
return promotionRepository
.findByCode(code)
.orElseThrow(() -> new NotFoundException("Promotion not found"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.kampus.kbazaar.promotion.PromotionService;
import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.security.JwtAuthFilter;
import com.kampus.kbazaar.shopper.Shopper;
Expand Down Expand Up @@ -39,6 +40,7 @@ public class CartControllerTest {
@Autowired private MockMvc mockMvc;

@MockBean private CartService cartService;
@MockBean private PromotionService promotionService;

@Test
public void getCart_ReturnsOk() throws Exception {
Expand Down
68 changes: 68 additions & 0 deletions kbazaar/src/test/java/com/kampus/kbazaar/cart/CartServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.kampus.kbazaar.cart;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.doReturn;

import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.promotion.Promotion;
import com.kampus.kbazaar.shopper.ShopperService;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.*;

public class CartServiceTest {

@Mock private CartRepository cartRepository;

@Spy @InjectMocks private CartService underTest;

@Mock private ShopperService shopperService;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}

@Test
@DisplayName("should be able to apply discount")
void shouldBeAbleToApplyDiscount() {
String username = "TechNinja";
LocalDateTime ldt = LocalDateTime.now();

Promotion promotion = new Promotion();
promotion.setPromotionId(1L);
promotion.setCode("FIXEDAMOUNT10");
promotion.setName("Fixed Amount $10 Off Entire Cart");
promotion.setDescription("Get $10 off on your entire cart purchase.");
promotion.setStartDate(ldt);
promotion.setEndDate(ldt);
promotion.setDiscountType("FIXED_AMOUNT");
promotion.setDiscountAmount(BigDecimal.valueOf(10.00));
promotion.setMaxDiscountAmount(BigDecimal.valueOf(10.00));
promotion.setApplicableTo("ENTIRE_CART");
promotion.setProductSkus("");
promotion.setMinQuantity(null);
promotion.setFreeQuantity(null);
Product product1 =
new Product(
1L,
"Apple iPhone 12 Pro",
"MOBILE-APPLE-IPHONE-12-PRO",
BigDecimal.valueOf(10010),
1);
Cart cart = new Cart();
CartProduct cartProduct = new CartProduct(1L, product1, null, cart, 1);
cart.addProduct(cartProduct);

BigDecimal expectFinalTotalCost = BigDecimal.valueOf(10000.0);

doReturn(cart).when(underTest).findCartByUsername(Mockito.any());
CartResponse cartResponse = underTest.applyDiscount(username, promotion);

assertEquals(expectFinalTotalCost, cartResponse.getFinalTotalCost());
assertEquals(promotion.getCode(), cartResponse.getPromotionCodes().get(0));
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.kampus.kbazaar.promotion;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

Expand Down Expand Up @@ -43,17 +41,15 @@ public void setup() {
}

@Test
@DisplayName("should return all promotions")
public void shouldReturnAllPromotions() throws Exception {
@DisplayName("should return not found promotion endpoint")
public void shouldReturnNotFoundFromFeaturePromotionDisable() throws Exception {
// Given

// When & Then
when(promotionService.getAll()).thenReturn(new ArrayList<>());

mockMvc.perform(get("/api/v1/promotions").contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());

verify(promotionService, times(1)).getAll();
.andExpect(status().is4xxClientError());
}

@Test
Expand Down

0 comments on commit bc07125

Please sign in to comment.