diff --git a/src/main/java/group/rohlik/application/CartDiscountAdder.java b/src/main/java/group/rohlik/application/CartDiscountAdder.java index c2ba200..4bea917 100644 --- a/src/main/java/group/rohlik/application/CartDiscountAdder.java +++ b/src/main/java/group/rohlik/application/CartDiscountAdder.java @@ -7,7 +7,6 @@ import lombok.AllArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.util.Assert; @AllArgsConstructor @Service @@ -20,15 +19,8 @@ public class CartDiscountAdder { public void add(long cartId, String code) { Cart cart = cartRepository.findById(cartId).orElseThrow(); Discount discount = discountRepository.findByCode(code).orElseThrow(); - applyDiscount(cart, discount); + cart.applyDiscount(discount); cartRepository.save(cart); } - - public void applyDiscount(Cart cart, Discount discount) { - //@todo suspicious code - should we move this logic to cart entity? - Assert.isTrue(!cart.getDiscounts().contains(discount), "Discount already applied"); - - cart.getDiscounts().add(discount); - } } diff --git a/src/main/java/group/rohlik/entity/Cart.java b/src/main/java/group/rohlik/entity/Cart.java index 7695073..a01fbee 100644 --- a/src/main/java/group/rohlik/entity/Cart.java +++ b/src/main/java/group/rohlik/entity/Cart.java @@ -82,10 +82,13 @@ private double totalDiscountsPrice() { } public double totalPrice() { - return BigDecimal - .valueOf(totalLinesPrice() - totalDiscountsPrice()) - .setScale(2, RoundingMode.CEILING) - .doubleValue(); + return Math.max( + 0, + BigDecimal + .valueOf(totalLinesPrice() - totalDiscountsPrice()) + .setScale(2, RoundingMode.CEILING) + .doubleValue() + ); } public Integer quantityOfProduct(String sku) { @@ -96,4 +99,16 @@ public Integer quantityOfProduct(String sku) { .map(CartLine::getQuantity) .orElse(0); } + + public boolean hasDiscount(String name) { + return discounts + .stream() + .anyMatch(line -> line.getName().equals(name)); + } + + public void applyDiscount(Discount discount) { + Assert.isTrue(!discounts.contains(discount), "Discount already applied"); + + discounts.add(discount); + } } diff --git a/src/test/java/group/rohlik/acceptance/steps/CartSteps.java b/src/test/java/group/rohlik/acceptance/steps/CartSteps.java index 7e2f029..ee89a04 100644 --- a/src/test/java/group/rohlik/acceptance/steps/CartSteps.java +++ b/src/test/java/group/rohlik/acceptance/steps/CartSteps.java @@ -1,9 +1,7 @@ package group.rohlik.acceptance.steps; import group.rohlik.entity.Cart; -import group.rohlik.entity.CartLine; import group.rohlik.entity.CartRepository; -import group.rohlik.entity.Discount; import io.cucumber.gherkin.internal.com.eclipsesource.json.JsonObject; import io.cucumber.java.Before; import io.cucumber.java.en.Given; @@ -17,9 +15,6 @@ import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; -import java.math.BigDecimal; -import java.math.RoundingMode; - @RequiredArgsConstructor public class CartSteps { @@ -83,6 +78,35 @@ public void thereShouldNotBeProductInCart(String sku) { Assertions.assertTrue(cart.quantityOfProduct(sku) == 0, String.format("Product %s should not be present", sku)); } + @Then("there should be discount {string} in my cart") + public void thereShouldBeDiscountInCart(String name) { + Cart cart = currentCart(); + + Assertions.assertTrue(cart.hasDiscount(name), String.format("Discount %s should be present", name)); + } + + @Then("there shouldn't be discounts in my cart") + public void thereShouldNotBeDiscountsInCart() { + Cart cart = currentCart(); + + Assertions.assertEquals(0, cart.getDiscounts().size()); + } + + @When("I apply {string} discount to my cart") + public void iApplyDiscountToMyCart(String code) { + JsonObject body = new JsonObject(); + body.add("code", code); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + template.exchange( + String.format("/carts/%d/discounts", currentCartId), + HttpMethod.POST, + new HttpEntity<>(body.toString(), headers), + String.class + ); + } + private Cart currentCart() { return cartRepository.findById(currentCartId).orElseThrow(); } diff --git a/src/test/resources/features/add_discount_to_cart.feature b/src/test/resources/features/add_discount_to_cart.feature new file mode 100644 index 0000000..7eddf65 --- /dev/null +++ b/src/test/resources/features/add_discount_to_cart.feature @@ -0,0 +1,39 @@ +Feature: Add discount to cart + As a Rohlik customer + I want to apply discounts to my cart + so that I can save some money + + Background: + Given the following products exist: + | sku | name | price | + | 001 | potatoes | 2.5 | + | 002 | water | 0.95 | + And there is a cart discount "free shipping" for 1 euros with code "FREE-SHIPPING" + And there is a cart discount "special offer" for 10 euros with code "SPECIAL-OFFER" + And I have a cart + + Scenario: No discounts + When I add 2 units of product "001" to my cart + Then the cart's total cost should be 5.0 euros + But there shouldn't be discounts in my cart + + Scenario: Add discount + Given I add 2 units of product "001" to my cart + When I apply "FREE-SHIPPING" discount to my cart + Then the cart's total cost should be 4.0 euros + And there should be discount "free shipping" in my cart + + Scenario: Add discount only apply once + Given I add 2 units of product "001" to my cart + And I apply "FREE-SHIPPING" discount to my cart + When I apply "FREE-SHIPPING" discount to my cart + Then the cart's total cost should be 4.0 euros + And there should be discount "free shipping" in my cart + + Scenario: Cart total should be always positive + Given I add 2 units of product "001" to my cart + And I apply "FREE-SHIPPING" discount to my cart + When I apply "SPECIAL-OFFER" discount to my cart + Then the cart's total cost should be 0.0 euros + And there should be discount "free shipping" in my cart + And there should be discount "special offer" in my cart