Skip to content
This repository was archived by the owner on Apr 26, 2023. It is now read-only.

Commit 7ee02c6

Browse files
author
Félix Carpena
committed
discount codes (step3)
1 parent 4a4f75a commit 7ee02c6

9 files changed

Lines changed: 175 additions & 10 deletions

File tree

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package group.rohlik.application;
2+
3+
import group.rohlik.entity.Cart;
4+
import group.rohlik.entity.CartRepository;
5+
import group.rohlik.entity.Discount;
6+
import group.rohlik.entity.DiscountRepository;
7+
import lombok.AllArgsConstructor;
8+
import org.springframework.stereotype.Service;
9+
import org.springframework.transaction.annotation.Transactional;
10+
11+
@AllArgsConstructor
12+
@Service
13+
@Transactional
14+
public class CartDiscountAdder {
15+
16+
private final CartRepository cartRepository;
17+
private final DiscountRepository discountRepository;
18+
19+
public void add(long cartId, String code) {
20+
Cart cart = cartRepository.findById(cartId).orElseThrow();
21+
Discount discount = discountRepository.findByCode(code).orElseThrow();
22+
cart.applyDiscount(discount);
23+
24+
cartRepository.save(cart);
25+
}
26+
}

src/main/java/group/rohlik/application/CartLineAdder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public void add(long cartId, String sku, int quantity) {
2222
Cart cart = cartRepository.findById(cartId).orElseThrow();
2323
Product product = productRepository.findById(sku).orElseThrow();
2424
cart.addLine(product, quantity);
25-
cart.recalculateDiscounts(discountRepository.findAll());
25+
cart.recalculateAutomaticDiscounts(discountRepository.findAllAutomatics());
2626

2727
cartRepository.save(cart);
2828
}

src/main/java/group/rohlik/controller/CartController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package group.rohlik.controller;
22

33
import com.fasterxml.jackson.databind.JsonNode;
4+
import group.rohlik.application.CartDiscountAdder;
45
import group.rohlik.application.CartLineAdder;
56
import lombok.AllArgsConstructor;
67
import org.springframework.http.HttpStatus;
@@ -15,6 +16,7 @@
1516
public class CartController {
1617

1718
private final CartLineAdder cartLineAdder;
19+
private final CartDiscountAdder cartDiscountAdder;
1820

1921
@PostMapping(path = "/carts/{id}/lines", consumes = "application/json", produces = "application/json")
2022
public ResponseEntity addLine(@PathVariable long id, @RequestBody JsonNode payload) {
@@ -25,4 +27,13 @@ public ResponseEntity addLine(@PathVariable long id, @RequestBody JsonNode paylo
2527

2628
return new ResponseEntity<>(HttpStatus.OK);
2729
}
30+
31+
@PostMapping(path = "/carts/{id}/discounts", consumes = "application/json", produces = "application/json")
32+
public ResponseEntity addDiscount(@PathVariable long id, @RequestBody JsonNode payload) {
33+
String code = payload.get("code").textValue();
34+
35+
cartDiscountAdder.add(id, code);
36+
37+
return new ResponseEntity<>(HttpStatus.OK);
38+
}
2839
}

src/main/java/group/rohlik/entity/Cart.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,13 @@ private double totalDiscountsPrice() {
8080
.sum();
8181
}
8282

83-
public void recalculateDiscounts(List<Discount> discounts) {
84-
this.discounts.clear();
83+
public void recalculateAutomaticDiscounts(List<Discount> discounts) {
84+
List<Discount> toBeDeleted = this.discounts.stream().filter(Discount::isAutomatic).toList();
85+
toBeDeleted.forEach(this.discounts::remove);
8586
double price = totalLinesPrice();
8687
discounts
8788
.stream()
89+
.filter(Discount::isAutomatic)
8890
.filter(discount -> discount.getMinPrice() < price)
8991
.forEach(this.discounts::add);
9092
}
@@ -108,6 +110,12 @@ public boolean hasDiscount(long id) {
108110
.anyMatch(line -> line.getId() == id);
109111
}
110112

113+
public boolean hasDiscount(String name) {
114+
return discounts
115+
.stream()
116+
.anyMatch(line -> line.getName().equals(name));
117+
}
118+
111119
public Integer quantityOfProduct(String sku) {
112120
return lines
113121
.stream()
@@ -116,4 +124,10 @@ public Integer quantityOfProduct(String sku) {
116124
.map(CartLine::getQuantity)
117125
.orElse(0);
118126
}
127+
public void applyDiscount(Discount discount) {
128+
Assert.isTrue(!discount.isAutomatic(), "Discount can not be of type automatic");
129+
Assert.isTrue(!discounts.contains(discount), "Discount already applied");
130+
131+
discounts.add(discount);
132+
}
119133
}
Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
package group.rohlik.entity;
22

3-
import lombok.Data;
3+
import lombok.Getter;
44
import lombok.NoArgsConstructor;
55

66
import javax.persistence.Column;
77
import javax.persistence.Entity;
8+
import javax.persistence.EnumType;
9+
import javax.persistence.Enumerated;
810
import javax.persistence.GeneratedValue;
911
import javax.persistence.GenerationType;
1012
import javax.persistence.Id;
1113

1214
@Entity(name = "discounts")
13-
@Data
15+
@Getter
1416
@NoArgsConstructor
1517
public class Discount {
18+
1619
@Id
1720
@GeneratedValue(strategy = GenerationType.IDENTITY)
1821
private long id;
@@ -22,4 +25,39 @@ public class Discount {
2225
private float minPrice;
2326
@Column(nullable = false)
2427
private float amount;
28+
@Column
29+
private String code;
30+
@Column
31+
@Enumerated(EnumType.STRING)
32+
private DiscountType type;
33+
34+
public enum DiscountType {
35+
CODE, MIN_PRICE
36+
}
37+
38+
public static Discount minimumPriceDiscount(String name, float amount, float minPrice) {
39+
Discount discount = new Discount();
40+
discount.name = name;
41+
discount.amount = amount;
42+
discount.minPrice = minPrice;
43+
discount.code = null;
44+
discount.type = DiscountType.MIN_PRICE;
45+
46+
return discount;
47+
}
48+
49+
public static Discount codeDiscount(String name, float amount, String code) {
50+
Discount discount = new Discount();
51+
discount.name = name;
52+
discount.amount = amount;
53+
discount.minPrice = 0;
54+
discount.code = code;
55+
discount.type = DiscountType.CODE;
56+
57+
return discount;
58+
}
59+
60+
public boolean isAutomatic() {
61+
return !type.equals(DiscountType.CODE);
62+
}
2563
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
package group.rohlik.entity;
22

33
import org.springframework.data.jpa.repository.JpaRepository;
4+
import org.springframework.data.jpa.repository.Query;
5+
6+
import java.util.List;
7+
import java.util.Optional;
48

59
public interface DiscountRepository extends JpaRepository<Discount, Long> {
10+
11+
Optional<Discount> findByCode(String code);
12+
13+
@Query("SELECT d FROM discounts d WHERE d.type in ('MIN_PRICE')")
14+
List<Discount> findAllAutomatics();
615
}

src/test/java/group/rohlik/acceptance/steps/CartSteps.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,35 @@ public void thereShouldBeDiscountInCart(long discountId) {
8989
Assertions.assertTrue(cart.hasDiscount(discountId), String.format("Discount %s should be present", discountId));
9090
}
9191

92+
@Then("there should be discount {string} in my cart")
93+
public void thereShouldBeDiscountByNameInCart(String name) {
94+
Cart cart = currentCart();
95+
96+
Assertions.assertTrue(cart.hasDiscount(name), String.format("Discount %s should be present", name));
97+
}
98+
9299
@Then("there shouldn't be discounts in my cart")
93100
public void thereShouldNotBeDiscountsInCart() {
94101
Cart cart = currentCart();
95102

96103
Assertions.assertEquals(0, cart.getDiscounts().size());
97104
}
98105

106+
@When("I apply {string} discount to my cart")
107+
public void iApplyDiscountToMyCart(String code) {
108+
JsonObject body = new JsonObject();
109+
body.add("code", code);
110+
HttpHeaders headers = new HttpHeaders();
111+
headers.setContentType(MediaType.APPLICATION_JSON);
112+
113+
template.exchange(
114+
String.format("/carts/%d/discounts", currentCartId),
115+
HttpMethod.POST,
116+
new HttpEntity<>(body.toString(), headers),
117+
String.class
118+
);
119+
}
120+
99121
private Cart currentCart() {
100122
return cartRepository.findById(currentCartId).orElseThrow();
101123
}

src/test/java/group/rohlik/acceptance/steps/DiscountSteps.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,26 @@
22

33
import group.rohlik.entity.Discount;
44
import group.rohlik.entity.DiscountRepository;
5-
import io.cucumber.java.en.And;
65
import io.cucumber.java.en.Given;
76
import lombok.AllArgsConstructor;
87
import org.springframework.transaction.annotation.Transactional;
98

109
@AllArgsConstructor
1110
public class DiscountSteps {
11+
1212
private final DiscountRepository discountRepository;
1313

1414
@Given("there is a cart discount {string} for {float} euros with a minimum total price of {float} euros")
1515
@Transactional
1616
public void thereIsACartDiscountWithAMinimumTotalPrice(String name, float amount, float minPrice) {
17-
Discount discount = new Discount();
18-
discount.setName(name);
19-
discount.setAmount(amount);
20-
discount.setMinPrice(minPrice);
17+
Discount discount = Discount.minimumPriceDiscount(name, amount, minPrice);
18+
discountRepository.save(discount);
19+
}
20+
21+
@Given("there is a cart discount {string} for {float} euros with code {string}")
22+
@Transactional
23+
public void thereIsACartDiscountWithCode(String name, float amount, String code) {
24+
Discount discount = Discount.codeDiscount(name, amount, code);
2125
discountRepository.save(discount);
2226
}
2327
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Feature: Add discount to cart
2+
As a Rohlik customer
3+
I want to apply discounts to my cart
4+
so that I can save some money
5+
6+
Background:
7+
Given the following products exist:
8+
| sku | name | price |
9+
| 001 | potatoes | 2.5 |
10+
| 002 | water | 0.95 |
11+
And there is a cart discount "free shipping" for 1 euros with code "FREE-SHIPPING"
12+
And there is a cart discount "Amazing discount" for 1 euros with a minimum total price of 6 euros
13+
And I have a cart
14+
15+
Scenario: Add discount
16+
Given I add 2 units of product "001" to my cart
17+
When I apply "FREE-SHIPPING" discount to my cart
18+
Then the cart's total cost should be 4.0 euros
19+
And there should be discount "free shipping" in my cart
20+
21+
Scenario: Add discount only apply once
22+
Given I add 2 units of product "001" to my cart
23+
And I apply "FREE-SHIPPING" discount to my cart
24+
When I apply "FREE-SHIPPING" discount to my cart
25+
Then the cart's total cost should be 4.0 euros
26+
And there should be discount "free shipping" in my cart
27+
28+
Scenario: Mix automatic and code discounts
29+
Given I add 3 units of product "001" to my cart
30+
When I apply "FREE-SHIPPING" discount to my cart
31+
Then the cart's total cost should be 5.5 euros
32+
And there should be discount "free shipping" in my cart
33+
And there should be discount "Amazing discount" in my cart
34+
35+
Scenario: Code discounts are no recalculated
36+
Given I add 2 units of product "001" to my cart
37+
And I add 2 units of product "002" to my cart
38+
And I apply "FREE-SHIPPING" discount to my cart
39+
When I remove product "002" of my cart
40+
Then the cart's total cost should be 4.0 euros
41+
And there should be discount "free shipping" in my cart

0 commit comments

Comments
 (0)