diff --git a/domain-model.md b/domain-model.md new file mode 100644 index 000000000..441d80704 --- /dev/null +++ b/domain-model.md @@ -0,0 +1,119 @@ +User Stories + +1. +As a member of the public, +So I can order a bagel before work, +I'd like to add a specific type of bagel to my basket. + +2. +As a member of the public, +So I can change my order, +I'd like to remove a bagel from my basket. + +3. +As a member of the public, +So that I can not overfill my small bagel basket +I'd like to know when my basket is full when I try adding an item beyond my basket capacity. + +4. +As a Bob's Bagels manager, +So that I can expand my business, +I’d like to change the capacity of baskets. + +5. +As a member of the public +So that I can maintain my sanity +I'd like to know if I try to remove an item that doesn't exist in my basket. + +6. +As a customer, +So I know how much money I need, +I'd like to know the total cost of items in my basket. + +7. +As a customer, +So I know what the damage will be, +I'd like to know the cost of a bagel before I add it to my basket. + +8. +As a customer, +So I can shake things up a bit, +I'd like to be able to choose fillings for my bagel. + +9. +As a customer, +So I don't over-spend, +I'd like to know the cost of each filling before I add it to my bagel order. + +10. +As the manager, +So we don't get any weird requests, +I want customers to only be able to order things that we stock in our inventory. + +11. +As a customer, +So i know how much i'm saving +I want to see my total cost bfore and after applying discounts + +12. +As a customer, +So I know what i'm paying for +I want to see each individual idem on the receipt accompanied with the cost. + +Basket Class + +| Method Variables | Methods | Scenario | Output | User Story | +|----------------------------------|----------------------------------------------------------------------------------------------------|----------------------------------------------------------|------------------------------------------------------------------|-------------| +| ArrayList basketContent | addItem(String SKU) | Basket full | Print error, return false | 1, 3, 8, 10 | +| ArrayList inventory | | Basket not full | print cost of item, return true | | +| int totalPrice | | | | | +| int basketSize | removeItem(String SKU) | Basket contains item | remove item from basketContent, return true | 2, 5 | +| | | Basket doesnt' contain item | print error, return false | | +| | | | | | +| | changeBasketSize(int newSize) | newSize is negative number | print error, return false | 4 | +| | | newSize is a positive number | print success, return true | | +| | | | | | +| | checkPrice() | | Prints totalPrice | 6 | +| | | | | | +| | addFilling(String SKU) | SKU corresponds to a bagel in basket | print price of filling
add to basketContent
return true | 7, 9 | +| | | SKU doesn't exist in basket | print error, return false | | +| | | | | | +| | addDiscount() | No discounted items | Return the totalPrice (Unchanged) | 11 | +| | | Discounted items | return the new totalPrice | | +| | | | | | +| | printReciet() | Basket is empty | print error | 12 | +| | | Basket is not empty | print the receipt including discounts | | +| | | | | | +| | addFilling(String bagelSKU, String FillingSKU) | Bagel doesn't have a filling,
And filling SKU exists | Add filling to bagel object, return true | | +| | | Bagel doesn't have filling, but SKU doesn't exists | print error, return false | | +| | | | | | +| | addToMaps(Product p, Map countMap, Map priceMap) | | Adds the product p to the price and countmap | | +| | | | | | +| | printReceiptLines(String productType, Map countMap, Map priceMap) | | Prints receit based on content of countMap and priceMap | | +| | | | | | +| | calculateTotal() | basketContent is empty | return 0 | | +| | | basketContent is not empty | return total price of basket items (WIthout discounts) | | +| | | | | | +| | + +Product class + +| Method Variables | Methods | Scenario | Output | User stories | +|------------------|--------------|----------|---------------|--------------| +| int SKU | getSKU() | | print SKU | | +| int price | getPrice() | | print price | | +| String name | getName() | | print name | | +| String variant | getVariant() | | print variant | | +| | | | | | +| | | | | | + +The classes Coffee and Filling inherit from the product class, and have no unique variables or methods + +Bagel class + +| Method variables | Methods | Scenario | Output | user story | +|------------------|------------------------|----------------------------------------------------------|------------------------------------------|------------| +| Filling filling | getFilling() | Filling exists | return Filling object | 8 | +| | | Filling doesn't exist | print error, return null | | +| | | | | | +| | | | | | diff --git a/gleek-VdlCz2dQg2d4mnczqiReLw(1).png b/gleek-VdlCz2dQg2d4mnczqiReLw(1).png new file mode 100644 index 000000000..32f6b64f9 Binary files /dev/null and b/gleek-VdlCz2dQg2d4mnczqiReLw(1).png differ 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..7234464d3 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Bagel.java @@ -0,0 +1,25 @@ +package com.booleanuk.core; + +public class Bagel extends Product { + private Filling filling; + + public Bagel (String SKU, double price, String name, String variant){ + super(SKU, price, name, variant); + filling = null; + } + + public Filling getFilling() { + return filling; + } + + public void setFilling(Filling filling) { + this.filling = filling; + } + + @Override + public String toString() { + return super.toString()+ + " filling= " + filling + + '}'; + } +} 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..925a97f24 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Basket.java @@ -0,0 +1,265 @@ +package com.booleanuk.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class Basket { + private ArrayList inventory; + private ArrayList basketContent; + private int basketSize; + private double totalPrice; + + public Basket(){ + this.inventory = new ArrayList<>(); + this.basketContent = new ArrayList<>(); + this.basketSize = 3; + this.totalPrice = 0; + inventory.add(new Bagel("BGLO", 0.49d, "Bagel", "Onion")); + inventory.add(new Bagel("BGLP", 0.39d, "Bagel", "Plain")); + inventory.add(new Bagel("BGLE", 0.49d, "Bagel", "Everything")); + inventory.add(new Bagel("BGLS", 0.49d, "Bagel", "Sesame")); + inventory.add(new Coffee("COFB", 0.99d, "Coffee", "Black")); + inventory.add(new Coffee("COFW", 1.19d, "Coffee", "White")); + inventory.add(new Coffee("COFC", 1.29d, "Coffee", "Cappuccino")); + inventory.add(new Coffee("COFL", 1.29d, "Coffee", "Latte")); + inventory.add(new Filling("FILB", 0.12d, "Filling", "Bacon")); + inventory.add(new Filling("FILE", 0.12d, "Filling", "Egg")); + inventory.add(new Filling("FILC", 0.12d, "Filling", "Cheese")); + inventory.add(new Filling("FILX", 0.12d, "Filling", "Cream Cheese")); + inventory.add(new Filling("FILS", 0.12d, "Filling", "Smoked Salmon")); + inventory.add(new Filling("FILH", 0.12d, "Filling", "Ham")); + inventory.add(new Product("DIS1", 0.39d, "12 Bagels", "Ham")); + inventory.add(new Product("DIS2", 0.49d, "6 Bagels", "Ham")); + inventory.add(new Product("DIS3", 0.99d, "COffee - Bagel", "Ham")); + } + + public Boolean addItem(String SKU){ + if(basketContent.size() >= basketSize){ + System.out.println("Basket full, cannot add more"); + return false; + } + + //TODO + //Replace instanceof with just .equals(p.getName()) + for (Product p: inventory){ + if (p.getSKU().equals(SKU)){ //Found product in inv, add to basket + if (!(p instanceof Filling)){ + if(p instanceof Bagel){ + basketContent.add(new Bagel(p.getSKU(), p.getPrice(), p.getName(), p.getVariant())); + totalPrice += p.getPrice(); + System.out.println("Bagel " + p.getVariant() + " added to basket at a cost of " + p.getPrice()); + } + if(p instanceof Coffee){ + basketContent.add(new Coffee(p.getSKU(), p.getPrice(), p.getName(), p.getVariant())); + totalPrice += p.getPrice(); + System.out.println("Coffee " + p.getVariant() + " added to basket at a cost of " + p.getPrice()); + } + return true; + }else{ + System.out.println("Cannot buy filling on it's own, add it to a bagel"); + return false; + } + } + } + + System.out.println("Couldn't find item in inventory"); + return false; + } + + public Boolean removeItem(String SKU){ + for (Product p: basketContent){ + if(p.getSKU().equals(SKU)){ + basketContent.remove(p); + totalPrice -= p.getPrice(); + return true; + } + } + + System.out.println("Couldn't find the item in the basket"); + return false; + } + + public int changeBasketSize(int newSize){ + setBasketSize(newSize); + return newSize; + } + + public Boolean addFilling(String bagelSKU, String fillingSKU){ + for (Product p: basketContent){ + if(p.getSKU().equals(bagelSKU) && (p instanceof Bagel)){ + //found bagel, now see if filling is in inventory + for (Product filling: inventory){ + if(filling.getSKU().equals(fillingSKU) && ((Bagel) p).getFilling() == null){ + System.out.println("Adding filling " + filling.getVariant() + " to " + p.getVariant() + " bagel at a cost of " + filling.getPrice()); + ((Bagel) p).setFilling((Filling) filling); + totalPrice += filling.getPrice(); + return true; + } + } + } + } + System.out.println("Couldn't find the bagel in basket"); + return false; + } + + //TODO + //REfactor addDiscount() to use helper methods and reduce code repetition + public double addDiscount() { + int bagelCounter = 0; + int coffeeCounter = 0; + ArrayList discountedBagelsList = new ArrayList<>(); + ArrayList coffeeList = new ArrayList<>(); + + //Count bagels + for (Product p : basketContent) { + if (p.getName().equals("Bagel")) { + bagelCounter += 1; + discountedBagelsList.add((Bagel) p); + } + if (p.getName().equals("Coffee")) { + coffeeCounter += 1; + coffeeList.add(p); + } + } + + //Figure out how many discounts and remaining items after discounts + int bagelDiscounts12 = bagelCounter / 12; // 20 / 12 = 1 + int bagelsAfterDiscount12 = bagelCounter % 12; // 20 % 12 = 8 + + + for (int i = 0; i < bagelDiscounts12; i++){ + //remove the cost of the 12 individual bagels, + for(int j = 0; j < 12; j++){ + totalPrice -= discountedBagelsList.get(j).getPrice(); // Deducting the price of the bagel from the total + } + //remove the 12 discounted bagels + for(int j = 0; j < 12; j++){ + discountedBagelsList.removeFirst(); + } + totalPrice+= 3.99; // For each 12 stack discount, add this price to the total + } + + if(bagelsAfterDiscount12 >= 6){ + //apply the 6 bagel discount + for(int j = 0; j < 6; j++){ + totalPrice -= discountedBagelsList.get(j).getPrice(); + } + totalPrice += 2.49; + bagelsAfterDiscount12 -= 6; + } + + //Calculate coffee + bagel pairs for the remaining discounts + for(int i = 0; i < bagelsAfterDiscount12; i++){ + if(!coffeeList.isEmpty()){ + totalPrice -= discountedBagelsList.get(i).getPrice(); + totalPrice -= coffeeList.get(i).getPrice(); + totalPrice += 1.25d; + } + } + + System.out.println("Total price after discounts " + String.format("%.02f",totalPrice)); + + return totalPrice; + } + + public void printReceipt() { + // Maps to store the count and price for each bagel variant + Map bagelVariantCount = new HashMap<>(); + Map bagelVariantPrice = new HashMap<>(); + Map coffeeVariantCount = new HashMap<>(); + Map coffeeVariantPrice = new HashMap<>(); + Map fillingVariantCount = new HashMap<>(); + Map fillingVariantPrice = new HashMap<>(); + + System.out.println(); + System.out.println(" ~~~ Bob's Bagels ~~~ "); + System.out.println("----------------------------"); + + // Loop through basket items and count the products, add it to count maps + for (Product p : basketContent) { + if (p.getName().equals("Bagel")) { + addToMaps(p, bagelVariantCount, bagelVariantPrice); + + // if bagel doesn't hava filling + if (((Bagel) p).getFilling() != null) { + addToMaps(p, fillingVariantCount, fillingVariantPrice); + } + + } else if (p.getName().equals("Coffee")) { + addToMaps(p, coffeeVariantCount, coffeeVariantPrice); + } + } + + printReceiptLines("Bagels", bagelVariantCount, bagelVariantPrice); + printReceiptLines("Fillings", fillingVariantCount, fillingVariantPrice); + printReceiptLines("Coffees", fillingVariantCount, fillingVariantPrice); + + double originalPrice = calculateTotal(); + //double originalPrice = calculateTotal(bagelVariantPrice) + calculateTotal(fillingVariantPrice) + calculateTotal(coffeeVariantPrice); //Recalculate undiscounted price (Not ideal solution) + double savings = originalPrice - totalPrice; + System.out.println("----------------------------"); + System.out.println("Original price: $" + String.format("%.02f", originalPrice)); + System.out.println("Price after discounts: $" + String.format("%.02f", totalPrice)); // Totalprice calculated in Discount + + if(savings > 0){ + System.out.println("You saved a total of: $" + String.format("%.02f", savings) + " on this shop :)"); + } + } + + private void addToMaps(Product p, Map countMap, Map priceMap){ + String variant = p.getVariant(); + countMap.put(variant, countMap.getOrDefault(variant, 0) +1); + priceMap.put(variant, priceMap.getOrDefault(variant, 0.0) + p.getPrice()); + + } + + private void printReceiptLines(String productType, Map countMap, Map priceMap){ + for (String variant: countMap.keySet()){ + int count = countMap.get(variant); //Amount of products in the order + double price = priceMap.get(variant); + if(count > 0){ + System.out.println(count + "X " + variant + " " + productType + " = $" + String.format("%.02f", price)); + } + } + } + + private double calculateTotal(){ + double total = 0; + for (Product p: basketContent){ + total += p.getPrice(); + if(p.getName().equals("Bagel")){ + if(((Bagel) p).getFilling()!= null){ + total += ((Bagel) p).getFilling().getPrice(); + } + + } + } + return total; + } + + + private int getBasketSize(){ + return this.basketSize; + } + + private void setBasketSize(int newSize){ + this.basketSize = newSize; + } + + public double getTotalPrice() { + return totalPrice; + } + + public void setTotalPrice(double totalPrice) { + this.totalPrice = totalPrice; + } + + public ArrayList getBasketContent() { + return basketContent; + } + + public void setBasketContent(ArrayList basketContent) { + this.basketContent = basketContent; + } +} 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..a23d5fb7b --- /dev/null +++ b/src/main/java/com/booleanuk/core/Coffee.java @@ -0,0 +1,10 @@ +package com.booleanuk.core; + +public class Coffee extends Product{ + + + public Coffee(String SKU, double price, String name, String variant) { + super(SKU, price, name, variant); + } + +} 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..e2784d7c5 --- /dev/null +++ b/src/main/java/com/booleanuk/core/Filling.java @@ -0,0 +1,14 @@ +package com.booleanuk.core; + +public class Filling extends Product{ + + + public Filling(String SKU, double price, String name, String variant) { + super(SKU, price, name, variant); + } + + @Override + public String toString() { + return getVariant(); + } +} diff --git a/src/main/java/com/booleanuk/core/Product.java b/src/main/java/com/booleanuk/core/Product.java new file mode 100644 index 000000000..7df4c079d --- /dev/null +++ b/src/main/java/com/booleanuk/core/Product.java @@ -0,0 +1,58 @@ +package com.booleanuk.core; + +public class Product { + + private String SKU; + private double price; + private String name; + private String variant; + + public Product(String SKU, double price, String name, String variant) { + this.SKU = SKU; + this.price = price; + this.name = name; + this.variant = variant; + } + + public String getSKU() { + return SKU; + } + + public void setSKU(String SKU) { + this.SKU = SKU; + } + + public double getPrice() { + return price; + } + + public void setPrice(double price) { + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVariant() { + return variant; + } + + public void setVariant(String variant) { + this.variant = variant; + } + + @Override + public String toString() { + return "Product{" + + "SKU='" + SKU + '\'' + + ", price=" + price + + ", name='" + name + '\'' + + ", variant='" + variant + '\'' + + '}'; + } +} diff --git a/src/test/java/com/booleanuk/core/BasketTest.java b/src/test/java/com/booleanuk/core/BasketTest.java new file mode 100644 index 000000000..5a7d0368e --- /dev/null +++ b/src/test/java/com/booleanuk/core/BasketTest.java @@ -0,0 +1,155 @@ +package com.booleanuk.core; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class BasketTest { + + @Test + void addItemTest(){ + + Basket basket = new Basket(); + + + Assertions.assertTrue(basket.addItem("BGLO")); + + } + + @Test + void removeItemTest(){ + Basket basket = new Basket(); + basket.addItem("BGLO"); + + + Assertions.assertTrue(basket.removeItem("BGLO")); + } + + @Test + void changeBasketSizeTest(){ + Basket basket = new Basket(); + basket.addItem("BGLO"); + + + Assertions.assertEquals(12, basket.changeBasketSize(12)); + } + + @Test + void checkPriceTest(){ + Basket basket = new Basket(); + basket.addItem("BGLO"); + + + Assertions.assertEquals(0.49, basket.getTotalPrice()); + } + + @Test + void addFilling(){ + Basket basket = new Basket(); + basket.changeBasketSize(20); + basket.addItem("BGLO"); + basket.addItem("BGLP"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + + basket.addFilling("BGLE", "FILC"); + basket.addFilling("BGLE", "FILC"); + + Assertions.assertTrue(basket.addFilling("BGLO", "FILS")); + + } + + @Test + void addDiscountTest(){ + Basket basket = new Basket(); + basket.changeBasketSize(20); + + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("COFB"); + basket.addFilling("BGLP", "FILC"); + + double newTotal = basket.addDiscount(); + + Assertions.assertEquals(5.36, newTotal, 0.005d); + } + + + @Test + void addDiscountWithFilling(){ + Basket basket = new Basket(); + basket.changeBasketSize(20); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addFilling("BGLP", "FILC"); + basket.addFilling("BGLP", "FILC"); + + double newTotal = basket.addDiscount(); + + Assertions.assertEquals(6.72, newTotal, 0.005d); + } + + @Test + void addDiscountVariedBasket(){ + Basket basket = new Basket(); + basket.changeBasketSize(30); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLP"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + basket.addItem("BGLE"); + basket.addItem("BGLO"); + basket.addItem("BGLO"); + basket.addItem("BGLO"); + basket.addItem("COFB"); + basket.addItem("COFB"); + basket.addItem("COFB"); + basket.addFilling("BGLE", "FILB"); + basket.addFilling("BGLE", "FILB"); + + + double newTotal = basket.addDiscount(); + basket.printReceipt(); + + Assertions.assertEquals(9.95, newTotal, 0.005d); + } + + +}