Skip to content

Commit

Permalink
finish add product api
Browse files Browse the repository at this point in the history
  • Loading branch information
Bank authored and Bank committed Mar 24, 2024
1 parent c7b4729 commit f54f417
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 54 deletions.

This file was deleted.

70 changes: 69 additions & 1 deletion kbazaar/src/main/java/com/kampus/kbazaar/cart/Cart.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.promotion.Promotion;
import com.kampus.kbazaar.promotion.PromotionDiscount;
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 lombok.AllArgsConstructor;
import lombok.Getter;
Expand All @@ -21,7 +25,7 @@ public class Cart {
private Long id;

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

@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(
Expand All @@ -34,9 +38,73 @@ public class Cart {
@JoinColumn(name = "shopper_id", referencedColumnName = "id")
private Shopper shopper;

public BigDecimal getEntireCartDiscountAmount() {
BigDecimal totalCost = this.getTotalCost();
Integer allProductQuantity = this.getAllProductQuantity();
return this.getPromotions().stream()
.filter(prom -> prom.getApplicableTo().equals("ENTIRE_CART"))
.reduce(
BigDecimal.ZERO,
(acc, prom) ->
acc.add(
PromotionDiscount.getDiscountAmountFromPromotion(
totalCost, allProductQuantity, prom)),
BigDecimal::add);
}

public Integer getAllProductQuantity() {
return this.getCartProducts().size();
}

public BigDecimal getTotalCost() {
return this.getCartProducts().stream()
.map(CartProduct::getFinalPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
}

public BigDecimal getFinalTotalCost() {
return this.getTotalCost().subtract(this.getEntireCartDiscountAmount());
}

public Cart addPromotion(Promotion promotion) {
this.promotions.add(promotion);
promotion.getCarts().add(this);
return this;
}

public Optional<CartProduct> addPromotionToProduct(Promotion promotion, String targetSku) {
Optional<CartProduct> cartProduct =
this.getCartProducts().stream()
.filter(filtered -> filtered.getProduct().getSku().equals(targetSku))
.findFirst();

cartProduct.ifPresent(cp -> cp.setPromotion(promotion));
return cartProduct;
}

public Cart addProduct(CartProduct cartProduct) {
Optional<CartProduct> existingCartProduct =
this.cartProducts.stream()
.filter(
filtered ->
filtered.getProduct()
.getId()
.equals(cartProduct.getProduct().getId()))
.findFirst();

if (existingCartProduct.isPresent()) {
CartProduct existingCartProductFirst = existingCartProduct.get();
existingCartProductFirst.setQuantity(
existingCartProductFirst.getQuantity() + cartProduct.getQuantity());
return this;
}

cartProduct.setCart(this);
this.cartProducts.add(cartProduct);
return this;
}

public CartResponse toCartResponse() {
return CartResponse.fromCart(this);
}
}
26 changes: 9 additions & 17 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartController.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.shopper.ShopperService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
Expand All @@ -10,30 +9,23 @@
@RequiredArgsConstructor
public class CartController {

private final ShopperService shopperService;
private final CartService cartService;

@GetMapping("/carts")
public ResponseEntity getCart() { // NOSONAR
return ResponseEntity.ok().build();
}

@PostMapping("/carts/{username}/products")
public ResponseEntity<String> addProduct(
@PostMapping("/carts/{username}/items")
public CartResponse addProduct(
@PathVariable("username") String username,
@RequestBody AddProductRequest addProductRequest) {

// Cart cart = cartService.findCartByUsername(username);
// Product product =
// productService.getProductBySku(addProductRequest.getProductSku());
// if (product.getQuantity() < addProductRequest.getQuantity()) {
// return ResponseEntity.badRequest().build();
// }

// return ResponseEntity.ok(cartService
// .findCartByUsername(username));
// .addProduct(product)
// .toAddProductResponse());

return ResponseEntity.ok("hey");
return cartService
.addProductByUsernameAndProductSku(
username,
addProductRequest.getProductSku(),
addProductRequest.getQuantity())
.toCartResponse();
}
}
41 changes: 37 additions & 4 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartProduct.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,60 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.promotion.Promotion;
import com.kampus.kbazaar.promotion.PromotionDiscount;
import jakarta.persistence.*;
import java.math.BigDecimal;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class CartProduct {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Column(name = "product_id")
private Long productId;
@ManyToOne
@JoinColumn(name = "product_id")
private Product product;

@Column(name = "quantity")
private Integer quantity;
@ManyToOne
@JoinColumn(name = "promotion_id")
private Promotion promotion;

@ManyToOne
@JoinColumn(name = "cart_id")
private Cart cart;

@Column(name = "quantity")
private Integer quantity;

public BigDecimal getDiscountAmount() {
return PromotionDiscount.getDiscountAmountFromPromotion(
product.getPrice(), quantity, promotion);
}

public BigDecimal getPrice() {
return product.getPrice().multiply(BigDecimal.valueOf(quantity));
}

public BigDecimal getFinalPrice() {
return this.getPrice().subtract(this.getDiscountAmount());
}

public static Optional<CartProduct> of(Product product, Integer quantity, Cart cart) {
if (product.getQuantity() < quantity) {
return Optional.empty();
}
return Optional.of(
CartProduct.builder().product(product).quantity(quantity).cart(cart).build());
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.shopper.Shopper;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CartRepository extends JpaRepository<Cart, Long> {}
public interface CartRepository extends JpaRepository<Cart, Long> {
Cart findByShopper(Shopper shopper);
}
56 changes: 56 additions & 0 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.promotion.Promotion;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Builder
@Data
public class CartResponse {
private Long cartId;
private List<Item> items;
private List<String> promotionCodes;
private BigDecimal totalCost;
private BigDecimal entireCartPromotionDiscount;
private BigDecimal finalTotalCost;

public static CartResponse fromCart(Cart cart) {
List<Item> items = cart.getCartProducts().stream().map(CartResponse::initItem).toList();

return CartResponse.builder()
.cartId(cart.getId())
.items(items)
.promotionCodes(cart.getPromotions().stream().map(Promotion::getCode).toList())
.totalCost(cart.getTotalCost())
.entireCartPromotionDiscount(cart.getEntireCartDiscountAmount())
.finalTotalCost(cart.getFinalTotalCost())
.build();
}

private static Item initItem(CartProduct cartProduct) {
return new Item(
cartProduct.getId(),
cartProduct.getProduct().getName(),
cartProduct.getQuantity(),
cartProduct.getPrice(),
cartProduct.getDiscountAmount(),
cartProduct.getFinalPrice(),
Optional.ofNullable(cartProduct.getPromotion()).map(Promotion::getCode).orElse(""));
}
}

record Item(
Long id,
String name,
Integer quantity,
BigDecimal price,
BigDecimal discount,
BigDecimal finalPrice,
String promotionCode) {}
27 changes: 26 additions & 1 deletion kbazaar/src/main/java/com/kampus/kbazaar/cart/CartService.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,39 @@
package com.kampus.kbazaar.cart;

import com.kampus.kbazaar.exceptions.InternalServerException;
import com.kampus.kbazaar.exceptions.NotFoundException;
import com.kampus.kbazaar.product.Product;
import com.kampus.kbazaar.product.ProductService;
import com.kampus.kbazaar.shopper.ShopperService;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class CartService {
private final ShopperService shopperService;
private final ProductService productService;
private final CartRepository cartRepository;

public Cart findCartByUsername(String username) {
return null;
return Optional.of(username)
.map(shopperService::getShopperByUsername)
.map(cartRepository::findByShopper)
.orElseThrow(() -> new NotFoundException("Failed to find cart by username"));
}

public Cart addProductByUsernameAndProductSku(
String username, String productSku, Integer quantity) {
Product product = productService.getProductBySku(productSku);
Cart cart = findCartByUsername(username);
// TODO: add deduct product stock logic here
return CartProduct.of(product, quantity, cart)
.map(cart::addProduct)
.map(cartRepository::save)
.orElseThrow(
() ->
new InternalServerException(
"Product out of stock or failed to add product to cart"));
}
}
27 changes: 0 additions & 27 deletions kbazaar/src/main/java/com/kampus/kbazaar/cart/CartTemp.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.kampus.kbazaar.promotion;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class PromotionDiscount {
public static BigDecimal getDiscountAmountFromPromotion(
BigDecimal price, Integer quantity, Promotion promotion) {
if (promotion == null) {
return BigDecimal.ZERO;
}
return switch (promotion.getDiscountType()) {
case "FIXED_AMOUNT" -> promotion.getDiscountAmount();
case "PERCENTAGE" -> price.subtract(
price.multiply(promotion.getDiscountAmount())
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP));
case "buy1_get1" -> {
if (quantity >= 2) {
yield price;
}
yield BigDecimal.ZERO;
}
case "buy2_get1" -> {
if (quantity >= 3) {
yield price;
}
yield BigDecimal.ZERO;
}
default -> BigDecimal.ZERO;
};
}

public static BigDecimal divideDiscount(BigDecimal price, Integer divider) {
return price.divide(BigDecimal.valueOf(divider), 2, RoundingMode.HALF_UP);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ public ShopperResponse getByUsername(String username) {
.map(Shopper::toResponse)
.orElseThrow(() -> new NotFoundException("Shopper not found"));
}

public Shopper getShopperByUsername(String username) {
return shopperRepository
.findByUsername(username)
.orElseThrow(() -> new NotFoundException("Shopper not found"));
}
}

0 comments on commit f54f417

Please sign in to comment.