diff --git a/src/main/java/com/booleanuk/core/Bagel.java b/src/main/java/com/booleanuk/core/Bagel.java new file mode 100644 index 000000000..8520e66c2 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Bagel.java @@ -0,0 +1,32 @@ +package com.booleanuk.core; + +import java.util.ArrayList; +import java.util.List; + +public class Bagel extends Item { + + private List fillingList; + + public Bagel(String SKU, float price, String name, String variant) { + super(SKU, price, name, variant); + fillingList = new ArrayList<>(); + } + + public boolean addFilling(Filling f) { + // make void? or keep boolean and set max number of fillings? + fillingList.add(f); + return true; + } + + public List getFillingList() { + return this.fillingList; + } + + public float getPriceWithFillings() { + float updatedPrice = this.getPrice(); + for (Filling f : this.getFillingList()) { + updatedPrice += f.getPrice(); + } + return updatedPrice; + } +} diff --git a/src/main/java/com/booleanuk/core/Basket.java b/src/main/java/com/booleanuk/core/Basket.java new file mode 100644 index 000000000..9fbd5eba2 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Basket.java @@ -0,0 +1,225 @@ +package com.booleanuk.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class Basket { + + private int capacity; + private Map items; + + public Basket(int capacity) { + this.capacity = capacity; + items = new HashMap<>(); + } + + private boolean hasBGL6Offer() { + + if (this.items.isEmpty()) { + return false; + } + + int numBagels = 0; + for (Map.Entry entry : this.items.entrySet()) { + if (entry.getKey() instanceof Bagel) { + numBagels += entry.getValue(); + } + } + return numBagels >= 6; + } + + private boolean hasCOFBOffer() { + if (items.isEmpty() || items.size() < 2 || getCapacity() < 2) { + return false; + } + return this.items.keySet().stream().anyMatch(Coffee.class::isInstance) && this.items.keySet().stream().anyMatch(Bagel.class::isInstance); + } + + private boolean hasBGL12Offer() { + if (this.items.isEmpty()) { + return false; + } + + int numBagels = 0; + for (Map.Entry entry : this.items.entrySet()) { + + if (entry.getKey() instanceof Bagel) { + numBagels += entry.getValue(); + } + } + return numBagels >= 12; + } + + private int getDozens(Map items) { + + int bagelCount = 0; + for (Map.Entry entry : items.entrySet()) { + + if (entry.getKey() instanceof Bagel) { + bagelCount += entry.getValue(); + } + } + return bagelCount / 12; + } + + private int getSixes(Map items) { + + int bagelCount = 0; + for (Map.Entry entry : items.entrySet()) { + + if (entry.getKey() instanceof Bagel) { + bagelCount += entry.getValue(); + } + } + return bagelCount / 6; + } + + private int getCoffeeBagelCombos(Map items) { + + int bagelCount = 0; + for (Item it : items.keySet()) { + if (it instanceof Bagel) { + bagelCount ++; + } + } + + int coffeeCount = 0; + for (Item it : items.keySet()) { + if (it instanceof Coffee) { + coffeeCount ++; + } + } + return Math.min(bagelCount, coffeeCount); + } + + private void removeBagels(Map its, int countToRemove) { + + // have to make sure countToRemove and current count is checked!! + for (Item it : new ArrayList<>(its.keySet())) { + + if (it instanceof Bagel && countToRemove > 0) { + + int thisCount = its.get(it); + int remove = Math.min(thisCount, countToRemove); + its.put(it, thisCount - remove); + + if (its.get(it) == 0) { + its.remove(it); + } + } + } + } + + private void removeCoffeeAndBagels(Map its, int combos) { + + removeBagels(its, combos); + for (Item it : new ArrayList<>(its.keySet())) { + + if (it instanceof Coffee && combos > 0) { + + int thisCount = its.get(it); + int remove = Math.min(thisCount, combos); + its.put(it, thisCount - remove); + + if (its.get(it) == 0) { + its.remove(it); + } + } + } + } + + public Map getItems() { + return items; + } + + public boolean setCapacity(int newCap) { + + if (newCap < 0 || newCap > 100) { + return false; + } + this.capacity = newCap; + return true; + } + + public boolean add(Item item) { + if (this.isFull()) { + return false; + } + else { + if (this.items.containsKey(item)) { + int amount = this.items.get(item) + 1; + this.items.put(item, amount); + } + else { + this.items.put(item, 1); + } + return true; + } + } + + public boolean remove(Item item) { + if (!this.items.containsKey(item)) { + return false; + } + this.items.remove(item); + return true; + } + + public float getTotalCost() { + float total = 0f; + Map remainingItems = new HashMap<>(this.items); // copy of this.items + + // check 12 bagel offer + if (this.hasBGL12Offer()) { + + int dozens = getDozens(remainingItems); + total += dozens * 3.99f; + + removeBagels(remainingItems, dozens * 12); + } + // check 6 bagel offer + if (this.hasBGL6Offer()) { + + int sixes = getSixes(remainingItems); + total += sixes * 2.49f; + + removeBagels(remainingItems, sixes * 6); + } + + // check if remaining items can get coffee+bagel offer + if (hasCOFBOffer()) { + + int combos = getCoffeeBagelCombos(remainingItems); + total += combos * 1.25f; + + removeCoffeeAndBagels(remainingItems, combos); + } + + for (Item it : remainingItems.keySet()) { + + if (it instanceof Bagel) { + total += ((Bagel) it).getPriceWithFillings(); + } else { + total += it.getPrice(); + } + } + return total; + } + + public int getCapacity() { + return this.capacity - this.items.size(); + } + + public int getAvailableCapacity() { + // available after items are added + // getCapacity if items-map is empty + // check this first, make sure Manager does not screw it up + + return 0; + } + + public boolean isFull() { + return getCapacity() < this.items.size(); + } +} diff --git a/src/main/java/com/booleanuk/core/Coffee.java b/src/main/java/com/booleanuk/core/Coffee.java new file mode 100644 index 000000000..97d69f726 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Coffee.java @@ -0,0 +1,8 @@ +package com.booleanuk.core; + +public class Coffee extends Item { + public Coffee(String SKU, float price, String name, String variant) { + super(SKU, price, name, variant); + } + +} diff --git a/src/main/java/com/booleanuk/core/Customer.java b/src/main/java/com/booleanuk/core/Customer.java new file mode 100644 index 000000000..1f54be123 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Customer.java @@ -0,0 +1,35 @@ +package com.booleanuk.core; + +import java.util.List; + +public class Customer extends Guest { + + public Customer(String name) { + super(name); + } + + public float getTotalPriceBasket() { + return this.getBasket().getTotalCost(); + } + + public float getPriceBagel(Bagel b) { + return b.getPriceWithFillings(); + } + + public float getPriceFilling(Filling f) { + return f.getPrice(); + } + + public boolean setFillings(Bagel b, List fillingList) { + if (this.getBasket().isFull()) { + return false; + } + else if (!this.getBasket().getItems().containsKey(b)) { + return false; + } + for (Filling f : fillingList) { + b.addFilling(f); + } + return true; + } +} diff --git a/src/main/java/com/booleanuk/core/Filling.java b/src/main/java/com/booleanuk/core/Filling.java new file mode 100644 index 000000000..9fac31603 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Filling.java @@ -0,0 +1,8 @@ +package com.booleanuk.core; + +public class Filling extends Item { + public Filling(String SKU, float price, String name, String variant) { + super(SKU, price, name, variant); + } + +} diff --git a/src/main/java/com/booleanuk/core/Guest.java b/src/main/java/com/booleanuk/core/Guest.java new file mode 100644 index 000000000..41d8290ac --- /dev/null +++ b/src/main/java/com/booleanuk/core/Guest.java @@ -0,0 +1,33 @@ +package com.booleanuk.core; + +public class Guest { + // "MEMBER OF PUBLIC" + private String name; + private Basket basket; + private int defaultCap = 10; + + public Guest(String name) { + this.name = name; + this.basket = new Basket(defaultCap); + } + + public Basket getBasket() { + return this.basket; + } + + public boolean addToBasket(Item item) { + return this.basket.add(item); + } + + public boolean removeFromBasket(Item item) { + return this.basket.remove(item); + } + + public boolean guestBasketIsFull() { + return this.basket.isFull(); + } + + public boolean hasItemInBasket(Item item) { + return this.basket.getItems().containsKey(item); + } +} diff --git a/src/main/java/com/booleanuk/core/Inventory.java b/src/main/java/com/booleanuk/core/Inventory.java new file mode 100644 index 000000000..e5a9a90bf --- /dev/null +++ b/src/main/java/com/booleanuk/core/Inventory.java @@ -0,0 +1,4 @@ +package com.booleanuk.core; + +public enum Inventory { +} diff --git a/src/main/java/com/booleanuk/core/Item.java b/src/main/java/com/booleanuk/core/Item.java new file mode 100644 index 000000000..36b3367a6 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Item.java @@ -0,0 +1,31 @@ +package com.booleanuk.core; + +abstract public class Item { + private final String SKU; + private final float price; // methods for basketPrice for discounts + private final String name; + private final String variant; + + public Item(String SKU, float price, String name, String variant) { + this.SKU = SKU; + this.price = price; + this.name = name; + this.variant = variant; + } + + public String getSKU() { + return this.SKU; + } + + public float getPrice() { + return this.price; + } + + public String getName() { + return this.name; + } + + public String getVariant() { + return this.variant; + } +} diff --git a/src/main/java/com/booleanuk/core/Manager.java b/src/main/java/com/booleanuk/core/Manager.java new file mode 100644 index 000000000..e9700a247 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Manager.java @@ -0,0 +1,13 @@ +package com.booleanuk.core; + +public class Manager { + + public boolean setCapacity(Basket b, int cap) { + return b.setCapacity(cap); + } + + public boolean isInItems(Basket b) { + // make a map for inventory, check here + return false; + } +} diff --git a/src/main/java/com/booleanuk/core/class-diagram.md b/src/main/java/com/booleanuk/core/class-diagram.md new file mode 100644 index 000000000..828256090 --- /dev/null +++ b/src/main/java/com/booleanuk/core/class-diagram.md @@ -0,0 +1,50 @@ +## UUUUPDATE don't forget pls + +```mermaid +classDiagram + Inventory <|-- Item + Item <|-- Bagel + Item <|-- Filling + Customer <|-- Manager + + + class Item { + - SKU: String + - price: float + - name: String + + getSKU(): String + + getPrice(): float + + getName(): String + } + + class Bagel { + - fillings: List + + getFillings(): List + } + + class Filling { + + getPrice(): float + } + + enum Inventory { + + SKU: List + } + + class Basket { + - items: Map + - capacity: int + + add(Item): boolean + + remove(Item): boolean + + getTotalCost(): float + } + + class Customer { + + } + + class Manager { + + changeCapacity(Basket, int): boolean + + orderIsInItems(): boolean + } + +``` \ No newline at end of file diff --git a/src/main/java/com/booleanuk/core/domain-model.md b/src/main/java/com/booleanuk/core/domain-model.md new file mode 100644 index 000000000..2bbbe09be --- /dev/null +++ b/src/main/java/com/booleanuk/core/domain-model.md @@ -0,0 +1,49 @@ +| Classes | Members | Methods | Scenarios | Outputs | +|-----------|------------------------------------|-------------------------------------|-----------------------------------|----------------------------| +| Basket | Map, int capacity | add(Item it) | it not an Item | false | +| | | | it is an Item | true | +| | | remove(Item it) | it not an Item | false | +| | | | it is an Item & in Basket | true | +| | | | it is an Item not in Basket | false (+ print message?) | +| | | getTotalCost() | Basket empty | 0 | +| | | | Basket not empty | totalCost | +| | | getTotal(Basket b) | b qualified for special offer | float | +| | | | b not qualified for special offer | b.getTotalCost() | +| | | hasBGL6Offer(Basket b) | Basket has 6 bagels | true | +| | | | Basket has not 6 bagels | false | +| | | hasBGL12Offer(Basket b) | Basket has 12 bagels | true | +| | | | Basket has not 12 bagels | false | +| | | hasCOFBOffer(Basket b) | Basket has coffee && bagel | true | +| | | | Basket has not coffee && bagel | false | +| Item | String SKU, int price, String name | getSKU() | item not in inventory | null | +| | | | item in inventory | String | +| | | getPrice() | item not in inventory | null | +| | | | item in inventory | float | +| | | getName() | item not in inventory | null | +| | | | item in inventory | String | +| Bagel | List | setFilling(Filling[] f) | empty f | false (error: must choose) | +| | | | f not empty | true | +| Filling | | getPrice() | not a Filling | null | +| | | | is a Filling | price | +| Manager | | changeCapacity(Basket b, int cap) | b not a Basket | null | +| | | isInItems() | all items in Item | true | +| | | | not all items in Item | false | +| | | | b is a Basket | capacity += cap | +| Guest | String name | addToBasket(Bagel bagel) | bagel is in inventory | true | +| | | | bagel not in inventory | false | +| | | removeFromBasket(Bagel bagel) | bagel is in Basket | true | +| | | | bagel not in Basket | false | +| | | isBasketFull() | basket is full | true | +| | | | basket not full | false | +| | | hasItemInBasket(Item item) | basket has item | true | +| | | | basket does not have item | false | +| Customer | | getTotalCostBasket() | basket empty | 0 | +| | | | basket not empty | float | +| | | getPriceBagel(Bagel bagel) | bagel in inventory | float | +| | | | bagel not in inventory | 0 | +| | | setFillings(List fillings) | filling in inventory | true | +| | | | filling not in inventory | false | +| | | getPriceFilling(Filling filling) | filling in inventory | float | +| | | | filling not in inventory | false | +| Inventory | enum! how to list it here | ----- createInventory() maybe? | | | +| Receipt | String receipt | printReceipt() | at checkout | String | \ No newline at end of file diff --git a/src/test/java/com/booleanuk/core/CoreTest.java b/src/test/java/com/booleanuk/core/CoreTest.java new file mode 100644 index 000000000..09e0103bc --- /dev/null +++ b/src/test/java/com/booleanuk/core/CoreTest.java @@ -0,0 +1,263 @@ +package com.booleanuk.core; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class CoreTest { + + // GUEST + @Test + public void shouldAddToBasket() { + Guest g = new Guest("Gio"); + Bagel bg = new Bagel("A", 20, "Aa", "Ab"); + g.addToBasket(bg); + Assertions.assertTrue(g.getBasket().getItems().containsKey(bg)); + } + + @Test + public void shouldRemoveFromBasket() { + Guest g = new Guest("Gio"); + Bagel bg = new Bagel("A", 20, "Aa", "Ab"); + g.removeFromBasket(bg); + Assertions.assertFalse(g.getBasket().getItems().containsKey(bg)); + } + + @Test + public void shouldBeFull() { + Guest g = new Guest("Gio"); + Manager m = new Manager(); + m.setCapacity(g.getBasket(), 2); + g.addToBasket(new Bagel("A", 20, "Aa", "Ab")); + g.addToBasket(new Bagel("B", 20, "Bb", "Bc")); + Assertions.assertTrue(g.getBasket().isFull()); + } + + @Test + public void shouldNotHaveItemToRemove() { + Basket b = new Basket(2); + b.add(new Bagel("A", 20, "Aa", "Ab")); + b.add(new Bagel("B", 20, "Bb", "Bc")); + Item c = new Bagel("C", 100, "Cc", "Cd"); + Assertions.assertFalse(b.remove(c)); + } + + // CUSTOMER + @Test + public void shouldGetTotalPriceBasket() { + Customer c = new Customer("Dave"); + Bagel a = new Bagel("A", 20, "Aa", "Ab"); + Bagel b = new Bagel("B", 20, "Bb", "Bc"); + c.getBasket().add(a); + c.getBasket().add(b); + Assertions.assertEquals(a.getPrice()+b.getPrice(), c.getTotalPriceBasket()); + } + + @Test + public void shouldGetBagelPrice() { + Customer c = new Customer("Dave"); + Bagel a = new Bagel("A", 20, "Aa", "Ab"); + a.addFilling(new Filling("F", 20, "Ff", "Fg")); + c.getBasket().add(a); + } + + @Test + public void shouldGetFillingPrice() { + Customer c = new Customer("Dave"); + Bagel a = new Bagel("A", 20, "Aa", "Ab"); + Filling f = new Filling("F", 20, "Ff", "Fg"); + Assertions.assertEquals(f.getPrice(), c.getPriceFilling(f)); + } + + @Test + public void shouldSetFillings() { + Customer c = new Customer("Dave"); + Bagel a = new Bagel("A", 20, "Aa", "Ab"); + Filling f = new Filling("F", 20, "Ff", "Fg"); + Filling g = new Filling("F", 20, "Ff", "Fg"); + c.getBasket().add(a); + c.setFillings(a, List.of(f, g)); + Assertions.assertEquals(List.of(f, g), a.getFillingList()); + } + + @Test + public void shouldGetPriceWithFillings() { + Customer c = new Customer("Dave"); + Bagel a = new Bagel("A", 20, "Aa", "Ab"); + Filling f = new Filling("F", 20, "Ff", "Fg"); + Filling g = new Filling("F", 20, "Ff", "Fg"); + c.getBasket().add(a); + c.setFillings(a, List.of(f, g)); + Assertions.assertEquals(a.getPrice()+f.getPrice()+g.getPrice(), c.getPriceBagel(a)); + } + + // ITEM: Bagel, Coffee, Filling + @Test + public void shouldGetSKUItem() { + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 100, "Cc", "Cd"); + Filling f = new Filling("F", 20, "Ff", "Fg"); + Assertions.assertEquals("F", f.getSKU()); + Assertions.assertEquals("B", b.getSKU()); + Assertions.assertEquals("C", c.getSKU()); + } + + @Test + public void shouldGetPriceItem() { + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + Assertions.assertEquals(20, b.getPrice()); + Assertions.assertEquals(30, c.getPrice()); + Assertions.assertEquals(10, f.getPrice()); + } + + @Test + public void shouldGetNameItem() { + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 20, "Cc", "Cd"); + Filling f = new Filling("F", 5, "Ff", "Fg"); + Assertions.assertEquals("Aa", b.getName()); + Assertions.assertEquals("Cc", c.getName()); + Assertions.assertEquals("Ff", f.getName()); + } + + @Test + public void shouldGetVariant() { + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 20, "Ff", "Fg"); + Assertions.assertEquals("Ab", b.getVariant()); + Assertions.assertEquals("Cd", c.getVariant()); + Assertions.assertEquals("Fg", f.getVariant()); + } + + @Test + public void getPriceBagelWithFilling() { + Bagel b = new Bagel("B", 20, "Aa", "Ab"); + Filling f1 = new Filling("F", 10, "Ff", "Fg"); + Filling f2 = new Filling("F", 10, "Ff", "Fg"); + b.addFilling(f1); b.addFilling(f2); + Assertions.assertEquals(40, b.getPriceWithFillings()); + } + + // MANAGER + @Test + public void shouldSetNewCapacity() { + Manager m = new Manager(); + Basket b = new Basket(10); + Assertions.assertEquals(10, b.getCapacity()); + m.setCapacity(b, 5); + Assertions.assertEquals(5, b.getCapacity()); + } + + @Test + public void shouldFindInItems() { + Manager m = new Manager(); + Basket bas = new Basket(10); + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + bas.add(b); bas.add(c); bas.add(f); + // FIX THIS + // think I may need a map or something like in the Scrabble exercise + // predefined inventory to "match" if item is valid + } + + // BASKET + @Test + public void shouldNotBeFull() { + Basket bas = new Basket(10); + Assertions.assertFalse(bas.isFull()); + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + bas.add(b); bas.add(c); bas.add(f); + Assertions.assertFalse(bas.isFull()); + } + @Test + public void shouldAdd() { + Basket bas = new Basket(10); + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + bas.add(b); + bas.add(c); + bas.add(f); + Assertions.assertEquals(3, bas.getItems().size()); + } + + @Test + public void shouldRemove() { + Basket bas = new Basket(10); + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + bas.add(b); bas.add(c); bas.add(f); + bas.remove(c); + Assertions.assertEquals(2, bas.getItems().size()); + } + + @Test + public void shouldGetTotalCost() { + Basket bas = new Basket(10); + Item b = new Bagel("B", 20, "Aa", "Ab"); + Coffee c = new Coffee("C", 30, "Cc", "Cd"); + Filling f = new Filling("F", 10, "Ff", "Fg"); + bas.add(b); + bas.add(c); + bas.add(f); + Assertions.assertEquals(10+1.25f, bas.getTotalCost()); + } + + @Test + public void shouldGetCap() { + Basket bas = new Basket(10); + Assertions.assertEquals(10, bas.getCapacity()); + Manager m = new Manager(); + m.setCapacity(bas, 100); + Assertions.assertEquals(100, bas.getCapacity()); + } + + @Test + public void shouldHave12Offer() { + Basket b = new Basket(100); + for (int i = 0; i < 12; i ++) { + Bagel x = new Bagel("a", 0.5f, " ", " "); + b.add(x); + } + Assertions.assertEquals(3.99f, b.getTotalCost()); + } + + /* + // tested initially, but made the methods private only to use in basket.getTotalCost() + @Test + public void shouldHave6Offer() { + Basket b = new Basket(61); + for (int i = 0; i < 6; i ++) { + Bagel x = new Bagel("a", 0.5f, " ", " "); + b.add(x); + } + Assertions.assertTrue(b.hasBGL6Offer()); + } + + @Test + public void shouldHave12Offer() { + Basket b = new Basket(100); + for (int i = 0; i < 12; i ++) { + Bagel x = new Bagel("a", 0.5f, " ", " "); + b.add(x); + } + Assertions.assertTrue(b.hasBGL12Offer()); + } + + @Test + public void shouldHaveCOFBOffer() { + Basket b = new Basket(10); + b.add(new Bagel("a", 0.5f, " ", " ")); + b.add(new Coffee("a", 1.5f, " ", " ")); + Assertions.assertTrue(b.hasCOFBOffer()); + } + */ +}