diff --git a/.idea/misc.xml b/.idea/misc.xml index c9209a5..c08f7b9 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -3,7 +3,7 @@ - + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/checkout/AnyGoodsOffer.java b/src/checkout/AnyGoodsOffer.java deleted file mode 100644 index 8b11348..0000000 --- a/src/checkout/AnyGoodsOffer.java +++ /dev/null @@ -1,16 +0,0 @@ -package checkout; - -public class AnyGoodsOffer extends Offer { - public final int totalCost; - public final int points; - - public AnyGoodsOffer(int totalCost, int points) { - this.totalCost = totalCost; - this.points = points; - } - - @Override - public void apply(Check check) { - - } -} diff --git a/src/checkout/Brand.java b/src/checkout/Brand.java new file mode 100644 index 0000000..323e73c --- /dev/null +++ b/src/checkout/Brand.java @@ -0,0 +1,5 @@ +package checkout; + +public enum Brand { + VOLOSHKOVE_POLE +} diff --git a/src/checkout/ByBrand.java b/src/checkout/ByBrand.java new file mode 100644 index 0000000..3a5bb52 --- /dev/null +++ b/src/checkout/ByBrand.java @@ -0,0 +1,22 @@ +package checkout; + +public class ByBrand implements Condition { + Brand brand; + + public ByBrand(Brand brand) { + setBrand(brand); + } + + @Override + public boolean checkCondition(Check check) { + boolean marker = false; + for (Product p : check.getProducts()) { + if (p.brand == brand) marker = true; + } + return marker; + } + + public void setBrand(Brand brand) { + this.brand = brand; + } +} diff --git a/src/checkout/ByCategory.java b/src/checkout/ByCategory.java new file mode 100644 index 0000000..c773fa7 --- /dev/null +++ b/src/checkout/ByCategory.java @@ -0,0 +1,22 @@ +package checkout; + +public class ByCategory implements Condition { + Category category; + + public ByCategory(Category category) { + setCategory(category); + } + + @Override + public boolean checkCondition(Check check) { + boolean marker = false; + for(Product p : check.getProducts()) { + if (p.category == category) marker = true; + } + return marker; + } + + public void setCategory(Category category) { + this.category = category; + } +} diff --git a/src/checkout/Check.java b/src/checkout/Check.java index 31436e5..b6990b7 100644 --- a/src/checkout/Check.java +++ b/src/checkout/Check.java @@ -1,11 +1,14 @@ package checkout; +import java.time.LocalDate; import java.util.ArrayList; import java.util.List; public class Check { private List products = new ArrayList<>(); private int points = 0; + private int discount = 0; + private static LocalDate todayDate = LocalDate.now(); public int getTotalCost() { int totalCost = 0; @@ -15,6 +18,10 @@ public int getTotalCost() { return totalCost; } + public int getTotalCostWithDiscount() { + return getTotalCost() - discount/10; + } + void addProduct(Product product) { products.add(product); } @@ -33,4 +40,23 @@ int getCostByCategory(Category category) { .mapToInt(p -> p.price) .reduce(0, (a, b) -> a + b); } + + public int getPointsForBrand(Brand brand) { + return products.stream() + .filter(p -> p.brand == brand) + .mapToInt(p -> p.price) + .reduce(0, (a,b)-> a+b); + } + + public void addDiscount(int discount) { + this.discount = discount; + } + + public List getProducts() { + return products; + } + + public static LocalDate getTodayDate() { + return todayDate; + } } diff --git a/src/checkout/CheckoutService.java b/src/checkout/CheckoutService.java index 3ac7cbb..c525a22 100644 --- a/src/checkout/CheckoutService.java +++ b/src/checkout/CheckoutService.java @@ -1,10 +1,15 @@ package checkout; +import java.util.ArrayList; +import java.util.List; + public class CheckoutService { private Check check; + private List offers = new ArrayList<>(); public void openCheck() { + offers = new ArrayList<>(); check = new Check(); } @@ -16,23 +21,19 @@ public void addProduct(Product product) { } public Check closeCheck() { + useAllOffer(); Check closedCheck = check; check = null; return closedCheck; } + private void useAllOffer() { + if(this.offers.size()!=0) this.offers.forEach(offer -> + offer.apply(check)); + } + public void useOffer(Offer offer) { - offer.apply(check); - if (offer instanceof FactorByCategoryOffer) { - FactorByCategoryOffer fbOffer = (FactorByCategoryOffer) offer; - int points = check.getCostByCategory(fbOffer.category); - check.addPoints(points * (fbOffer.factor - 1)); - } else { - if (offer instanceof AnyGoodsOffer) { - AnyGoodsOffer agOffer = (AnyGoodsOffer) offer; - if (agOffer.totalCost <= check.getTotalCost()) - check.addPoints(agOffer.points); - } - } + if(check != null) + this.offers.add(offer); } } diff --git a/src/checkout/Condition.java b/src/checkout/Condition.java new file mode 100644 index 0000000..a47c266 --- /dev/null +++ b/src/checkout/Condition.java @@ -0,0 +1,5 @@ +package checkout; + +public interface Condition { + boolean checkCondition(Check check); +} diff --git a/src/checkout/DiscountByBrand.java b/src/checkout/DiscountByBrand.java new file mode 100644 index 0000000..d6a686a --- /dev/null +++ b/src/checkout/DiscountByBrand.java @@ -0,0 +1,25 @@ +package checkout; + +public class DiscountByBrand implements Reward { + private double discount; + private Brand brand; + + public DiscountByBrand(double discount, Brand brand) { + setBrand(brand); + setDiscount(discount); + } + + @Override + public void apply(Check check) { + Double points = check.getPointsForBrand(brand) * discount * 10; + check.addDiscount(points.intValue()); + } + + public void setBrand(Brand brand) { + this.brand = brand; + } + + public void setDiscount(double discount) { + this.discount = discount; + } +} diff --git a/src/checkout/FactorByCategoryOffer.java b/src/checkout/FactorByCategoryOffer.java deleted file mode 100644 index fee57f0..0000000 --- a/src/checkout/FactorByCategoryOffer.java +++ /dev/null @@ -1,16 +0,0 @@ -package checkout; - -public class FactorByCategoryOffer extends Offer { - final Category category; - final int factor; - - public FactorByCategoryOffer(Category category, int factor) { - this.category = category; - this.factor = factor; - } - - @Override - public void apply(Check check) { - - } -} diff --git a/src/checkout/FactorRewardByCategory.java b/src/checkout/FactorRewardByCategory.java new file mode 100644 index 0000000..7a5b91e --- /dev/null +++ b/src/checkout/FactorRewardByCategory.java @@ -0,0 +1,25 @@ +package checkout; + +public class FactorRewardByCategory implements Reward { + private int factor; + Category category; + + public FactorRewardByCategory(int factor, Category category) { + setCategory(category); + setFactor(factor); + } + + @Override + public void apply(Check check) { + int points = check.getCostByCategory(category); + check.addPoints(points * (factor-1)); + } + + public void setFactor(int factor) { + this.factor = factor; + } + + public void setCategory(Category category) { + this.category = category; + } +} diff --git a/src/checkout/FlatReward.java b/src/checkout/FlatReward.java new file mode 100644 index 0000000..617facf --- /dev/null +++ b/src/checkout/FlatReward.java @@ -0,0 +1,18 @@ +package checkout; + +public class FlatReward implements Reward { + private int reward; + + public FlatReward (int reward) { + setReward(reward); + } + + @Override + public void apply(Check check) { + check.addPoints(reward); + } + + public void setReward(int reward) { + this.reward = reward; + } +} diff --git a/src/checkout/Offer.java b/src/checkout/Offer.java index f2c67fe..506dbdf 100644 --- a/src/checkout/Offer.java +++ b/src/checkout/Offer.java @@ -1,5 +1,23 @@ package checkout; -public abstract class Offer { - public abstract void apply(Check check); +import java.time.LocalDate; + +public class Offer { + Reward reward; + Condition condition; + private LocalDate expirationDate; + + public Offer(Reward reward , Condition condition, LocalDate expirationDate) { + this.condition = condition; + this.reward = reward; + this.expirationDate = expirationDate; + } + + public void apply(Check check) { + if(condition.checkCondition(check) && isOfferAvailable(check.getTodayDate())) reward.apply(check); + } + + public boolean isOfferAvailable(LocalDate todayDate) { + return (expirationDate.isAfter(todayDate)); + } } diff --git a/src/checkout/Product.java b/src/checkout/Product.java index f03a6e8..fbfc6e1 100644 --- a/src/checkout/Product.java +++ b/src/checkout/Product.java @@ -4,11 +4,17 @@ public class Product { final int price; final String name; Category category; + Brand brand; - public Product(int price, String name, Category category) { + public Product(int price, String name, Category category, Brand brand) { this.price = price; this.name = name; this.category = category; + this.brand = brand; + } + + public Product(int price, String name, Category category) { + this(price, name, category, null); } public Product(int price, String name) { diff --git a/src/checkout/Reward.java b/src/checkout/Reward.java new file mode 100644 index 0000000..2e885a0 --- /dev/null +++ b/src/checkout/Reward.java @@ -0,0 +1,6 @@ +package checkout; + +public interface Reward { + + void apply(Check check); +} diff --git a/src/checkout/TotalCost.java b/src/checkout/TotalCost.java new file mode 100644 index 0000000..659eed5 --- /dev/null +++ b/src/checkout/TotalCost.java @@ -0,0 +1,18 @@ +package checkout; + +public class TotalCost implements Condition { + int totalCost; + + public TotalCost(int totalCost){ + setTotalCost(totalCost); + } + + @Override + public boolean checkCondition(Check check) { + return (totalCost <= check.getTotalCost()); + } + + public void setTotalCost(int totalCost) { + this.totalCost = totalCost; + } +} diff --git a/test/CheckoutServiceTest.java b/test/CheckoutServiceTest.java index a34315e..d343b01 100644 --- a/test/CheckoutServiceTest.java +++ b/test/CheckoutServiceTest.java @@ -1,7 +1,7 @@ import checkout.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - +import java.time.LocalDate; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -10,6 +10,7 @@ public class CheckoutServiceTest { private Product milk_7; private CheckoutService checkoutService; private Product bred_3; + private Product milk_7_WithBrand; @BeforeEach void setUp() { @@ -18,6 +19,7 @@ void setUp() { milk_7 = new Product(7, "Milk", Category.MILK); bred_3 = new Product(3, "Bred"); + milk_7_WithBrand = new Product(7, "Milk", Category.MILK, Brand.VOLOSHKOVE_POLE); } @Test @@ -42,7 +44,6 @@ void addProduct__whenCheckIsClosed__opensNewCheck() { checkoutService.addProduct(milk_7); Check milkCheck = checkoutService.closeCheck(); assertThat(milkCheck.getTotalCost(), is(7)); - checkoutService.addProduct(bred_3); Check bredCheck = checkoutService.closeCheck(); assertThat(bredCheck.getTotalCost(), is(3)); @@ -53,40 +54,97 @@ void closeCheck__calcTotalPoints() { checkoutService.addProduct(milk_7); checkoutService.addProduct(bred_3); Check check = checkoutService.closeCheck(); - assertThat(check.getTotalPoints(), is(10)); } @Test - void useOffer__addOfferPoints() { + void useOffer__whenCheckClose() { + checkoutService.useOffer(new Offer(new FlatReward(20), new TotalCost(10), LocalDate.now().plusDays(1))); + checkoutService.useOffer(new Offer(new FactorRewardByCategory(2, Category.MILK), + new ByCategory(Category.MILK), LocalDate.now().plusDays(1))); + checkoutService.addProduct(milk_7); + checkoutService.addProduct((bred_3)); checkoutService.addProduct(milk_7); - checkoutService.addProduct(bred_3); - - checkoutService.useOffer(new AnyGoodsOffer(6, 2)); Check check = checkoutService.closeCheck(); + assertThat(check.getTotalPoints(), is(51)); + } - assertThat(check.getTotalPoints(), is(12)); + @Test + void isOfferAvailable__ifTimeForOffersEnded__doNothing() { + checkoutService.addProduct(milk_7); + checkoutService.addProduct(milk_7); + checkoutService.addProduct((bred_3)); + Offer offer = new Offer(new FlatReward(20), new TotalCost(10), + LocalDate.now().minusDays(1)); + checkoutService.useOffer(offer); + Check check = checkoutService.closeCheck(); + assertThat(check.getTotalPoints(), is(17)); } + @Test + void useOffer__addOfferPoints() { + checkoutService.addProduct(milk_7_WithBrand); + checkoutService.addProduct(milk_7_WithBrand); + Offer offer = new Offer(new FlatReward(20), new TotalCost(10), + LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); + Check check = checkoutService.closeCheck(); + assertThat(check.getTotalPoints(), is(34)); + } @Test void useOffer__whenCostLessThanRequired__doNothing() { checkoutService.addProduct(bred_3); - - checkoutService.useOffer(new AnyGoodsOffer(6, 2)); + checkoutService.addProduct(bred_3); + checkoutService.addProduct(bred_3); + Offer offer = new Offer(new FlatReward(20), new TotalCost(10), + LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); Check check = checkoutService.closeCheck(); - - assertThat(check.getTotalPoints(), is(3)); + assertThat(check.getTotalPoints(), is(9)); } @Test void useOffer__factorByCategory() { - checkoutService.addProduct(milk_7); - checkoutService.addProduct(milk_7); + checkoutService.addProduct(milk_7_WithBrand); + checkoutService.addProduct(milk_7_WithBrand); + Offer offer = new Offer(new FactorRewardByCategory(2, Category.MILK), + new ByCategory(Category.MILK), LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); + Check check = checkoutService.closeCheck(); + assertThat(check.getTotalPoints(), is(28)); + } + + @Test + void useOffer__whenCategoryAbsent__doNothing() { + checkoutService.addProduct(bred_3); checkoutService.addProduct(bred_3); + Offer offer = new Offer(new FactorRewardByCategory(2, Category.MILK), + new ByCategory(Category.MILK), LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); + Check check = checkoutService.closeCheck(); + assertThat(check.getTotalPoints(), is(6)); + } - checkoutService.useOffer(new FactorByCategoryOffer(Category.MILK, 2)); + @Test + void useOffer__discountByBrand() { + checkoutService.addProduct(milk_7_WithBrand); + checkoutService.addProduct(milk_7_WithBrand); + Offer offer = new Offer(new DiscountByBrand(0.5, Brand.VOLOSHKOVE_POLE), + new ByBrand(Brand.VOLOSHKOVE_POLE), LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); + Check check = checkoutService.closeCheck(); + assertThat(check.getTotalCostWithDiscount(), is(7)); + } + + @Test + void useOffer__whenBrandAbsent__doNothing() { + checkoutService.addProduct(bred_3); + checkoutService.addProduct(milk_7); + Offer offer = new Offer(new DiscountByBrand(0.5, Brand.VOLOSHKOVE_POLE), + new ByBrand(Brand.VOLOSHKOVE_POLE), LocalDate.now().plusDays(1)); + checkoutService.useOffer(offer); Check check = checkoutService.closeCheck(); + assertThat(check.getTotalCostWithDiscount(), is(10)); - assertThat(check.getTotalPoints(), is(31)); } }