diff --git a/DomainModel.md b/DomainModel.md new file mode 100644 index 00000000..e6d09638 --- /dev/null +++ b/DomainModel.md @@ -0,0 +1,22 @@ +# Core Requirements + +| Classes | Methods/Properties | Scenario | Outputs +| ----------- | ------------------------------------------------------ | --------------------------------------- | --------------------------------- +| Product.cs | string SKU | Short code for name + variant | String with SKU code +| Product.cs | float Price | Pricing for each item | float with price in decimal +| Product.cs | enum Name | Product type for each item to get | Bagel, Coffee, Filling +| Product.cs | enum BagelVariant | Types of variant you can get with bagel | Onion, Plain, Everything, Sesame +| Product.cs | enum CoffeeVariant | Types of variant you can get with Coffee| Black, White, Cappucino, Latte +| Product.cs | enum FillingVariant | Types of variant you can get for Filling| Bacon, Egg, Cheese, CreamCheese, SmokedSalmon, Ham +| Basket.cs | List<\ProductList\> Basket | List to add each chosen item to | Task added to list +| Basket.cs | Guid id | Id to keep track of items in basket | Id for each item +| Basket.cs | bool IsFull | Bool to check to see if basket is full | True or False +| Basket.cs | int Capacity | Int to decide max amount of items | Int that dictates max items +| Basket.cs | AddItem(Product prodcut) | Add item to the basket list | Product added to list +| Basket.cs | RemoveItem(Guid itemId) | Remove specific item from basket | Specific item removed from list +| Basket.cs | ChangeCapacity(int Capacity) | Changes the total capacity of the basket| A changed version of the Capacity +| Checkout.cs | int TotalPrice | Int tracking the total price all items | Int in $$$ +| Checkout.cs | string Receipt | Clear version of all total costs written| Each item and it's costs written in console +| Checkout.cs | int Discount | Discount if enough items are bought | New price calculated with new discount +| Checkout.cs | string OrderConfirmation | Using twilio to print a conformation | SMS with order conformation +| Checkout.cs | CashOrCard() | Delivers the user their total | A console log with an overview of the transactions \ No newline at end of file diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs new file mode 100644 index 00000000..9f4047e5 --- /dev/null +++ b/exercise.main/Basket.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Basket + { + public List Products { get; set; } + public int Capacity { get; set; } + + public Basket(int capacity) + { + Capacity = capacity; + Products = new List(); + } + + public bool AddItem(Product product) + { + if (Products.Count >= Capacity) + { + return false; + } + + Products.Add(product); + return true; + } + + public bool RemoveItem(Product product) + { + var selectedItem = Products.FirstOrDefault(p => p.Id == product.Id); + if (selectedItem == null) + { + return false; + } + + Products.Remove(selectedItem); + return true; + } + + public void ChangeCapacity(int capacity) + { + Capacity = capacity; + } + } +} diff --git a/exercise.main/CheckOut.cs b/exercise.main/CheckOut.cs new file mode 100644 index 00000000..dec9cf59 --- /dev/null +++ b/exercise.main/CheckOut.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class CheckOut + { + public float TotalPrice(Basket basket) + { + var groupedBySKU = basket.Products.GroupBy(p => p.SKU); + + float total = 0; + + foreach (var group in groupedBySKU) + { + string sku = group.Key; + int amount = group.Count(); + float price = group.First().Price; + + if (Inventory.Discounts.ContainsKey(sku)) + { + var special = Inventory.Discounts[sku]; + int n = special.RequiredAmount; + float discountPrice = special.DiscountPrice; + + int sets = amount / n; + int remainder = amount % n; + + total += (sets * discountPrice) + (remainder * price); + } + else + { + total += amount * price; + } + + + } + + return total; + } + + public string Receipt(Basket basket) + { + + var receipt = new StringBuilder(); + receipt.AppendLine("\n~~~ Bob's Bagels ~~~\n"); + receipt.AppendLine(DateTime.Now.ToString() + "\n"); + receipt.AppendLine("----------------------------\n"); + + var groupedBySKU = basket.Products.GroupBy(p => p.SKU); + + foreach (var group in groupedBySKU) + { + string sku = group.Key; + int amount = group.Count(); + string itemName = group.First().Name.ToString(); + string variant = group.First().Variant.ToString(); + float price = group.First().Price; + + float total = 0; + + if (Inventory.Discounts.ContainsKey(sku)) + { + var special = Inventory.Discounts[sku]; + total = special.CalculateDiscount(amount, price); + } + else + { + total = amount * price; + } + + receipt.AppendLine($"{variant} {itemName} {amount} ${total:0.00}"); + } + + receipt.AppendLine("----------------------------\n"); + receipt.AppendLine($"Total: ${TotalPrice(basket):0.00}\n"); + receipt.AppendLine(" Thank you "); + receipt.AppendLine(" for your order "); + + return receipt.ToString(); + } + } +} diff --git a/exercise.main/Discount.cs b/exercise.main/Discount.cs new file mode 100644 index 00000000..f5b02606 --- /dev/null +++ b/exercise.main/Discount.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Discount + { + public int RequiredAmount { get; set; } + public float DiscountPrice { get; set; } + + public Discount(int requiredAmount, float discountPrice) + { + RequiredAmount = requiredAmount; + DiscountPrice = discountPrice; + } + + public float CalculateDiscount(int amount, float price) + { + int basketAmount = amount / RequiredAmount; + int remainder = amount % RequiredAmount; + + return (basketAmount * DiscountPrice) + (remainder * price); + } + } +} diff --git a/exercise.main/Inventory.cs b/exercise.main/Inventory.cs new file mode 100644 index 00000000..c43281aa --- /dev/null +++ b/exercise.main/Inventory.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public static class Inventory + { + public static Dictionary Bagels = new Dictionary() + { + { BagelVariant.Onion, 0.49f }, + { BagelVariant.Plain, 0.39f }, + { BagelVariant.Everything, 0.49f }, + { BagelVariant.Sesame, 0.49f } + }; + + public static Dictionary Coffees = new Dictionary() + { + { CoffeeVariant.Black, 0.99f }, + { CoffeeVariant.White, 1.19f }, + { CoffeeVariant.Cappucino, 1.29f }, + { CoffeeVariant.Latte, 1.29f } + }; + + public static Dictionary Fillings = new Dictionary() + { + { FillingVariant.Bacon, 0.12f }, + { FillingVariant.Egg, 0.12f }, + { FillingVariant.Cheese, 0.12f }, + { FillingVariant.CreamCheese, 0.12f }, + { FillingVariant.SmokedSalmon, 0.12f }, + { FillingVariant.Ham, 0.12f } + }; + + public static Dictionary Discounts = new Dictionary() + { + { "BGLO", new Discount(6, 2.49f) }, + { "BGLP", new Discount(12, 3.99f) }, + { "BGLE", new Discount(6, 2.49f) } + }; + + public static Product CreateBagel(BagelVariant variant) + { + if (!Bagels.ContainsKey(variant)) + { + throw new InvalidOperationException("Bagel not in inventory"); + } + + return new Product(ProductName.Bagel, variant, Bagels[variant]); + } + + public static Product CreateCoffee(CoffeeVariant variant) + { + if (!Coffees.ContainsKey(variant)) + { + throw new InvalidOperationException("Coffee not in inventory"); + } + + return new Product(ProductName.Coffee, variant, Coffees[variant]); + } + + public static Product CreateFilling(FillingVariant variant) + { + if (!Fillings.ContainsKey(variant)) + { + throw new InvalidOperationException("Filling not in inventory"); + } + + return new Product(ProductName.Filling, variant, Fillings[variant]); + } + } +} \ No newline at end of file diff --git a/exercise.main/Product.cs b/exercise.main/Product.cs new file mode 100644 index 00000000..48a2b123 --- /dev/null +++ b/exercise.main/Product.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + + public enum ProductName + { + Bagel, Coffee, Filling + } + + public enum BagelVariant + { + Onion, Plain, Everything, Sesame + } + + public enum CoffeeVariant + { + Black, White, Cappucino, Latte + } + + public enum FillingVariant + { + Bacon, Egg, Cheese, CreamCheese, SmokedSalmon, Ham + } + + public class Product + { + public string SKU { get; set; } + public float Price { get; set; } + public ProductName Name { get; set; } + public Enum Variant { get; set; } + public Guid Id { get; set; } + + public Product(ProductName name, Enum variant, float price) + { + Name = name; + Variant = variant; + Price = price; + SKU = CreateSKU(); + Id = Guid.NewGuid(); + } + + private string CreateSKU() + { + string nameCode = Name switch + { + ProductName.Bagel => "BGL", + ProductName.Coffee => "COF", + ProductName.Filling => "FIL" + }; + + return nameCode + Variant.ToString().Substring(0, 1).ToUpper(); + } + + } +} diff --git a/exercise.main/Program.cs b/exercise.main/Program.cs index 3751555c..fe1bf96b 100644 --- a/exercise.main/Program.cs +++ b/exercise.main/Program.cs @@ -1,2 +1,183 @@ -// See https://aka.ms/new-console-template for more information -Console.WriteLine("Hello, World!"); + +using exercise.main; +using Twilio.TwiML.Voice; + +class Program +{ + static void Main(string[] args) + { + var basket = new Basket(5); + + Console.WriteLine("Welcome To Bob's Burgers!\n"); + + while (true) + { + Console.WriteLine("What would you like: \n"); + Console.WriteLine("1. Add bagel to basket"); + Console.WriteLine("2. Add coffee to basket"); + Console.WriteLine("3. Remove bagel from basket"); + Console.WriteLine("4. Change basket capacity"); + Console.WriteLine("5. Show total cost"); + Console.WriteLine("6. Checkout basket"); + Console.WriteLine("7. Leave emptyhanded\n"); + + var checkout = new CheckOut(); + var input = Console.ReadLine(); + + switch (input) + { + case "1": + Console.WriteLine("\nWhat bagel do you want?\n"); + Console.WriteLine("1. Onion"); + Console.WriteLine("2. Plain"); + Console.WriteLine("3. Everything"); + Console.WriteLine("4. Sesame\n"); + var bagelInput = Console.ReadLine(); + switch (bagelInput) + { + case "1": + basket.AddItem(Inventory.CreateBagel(BagelVariant.Onion)); + Console.WriteLine("\nOnion bagel added!\n"); + break; + + case "2": + basket.AddItem(Inventory.CreateBagel(BagelVariant.Plain)); + Console.WriteLine("\nPlain bagel added!\n"); + break; + + case "3": + basket.AddItem(Inventory.CreateBagel(BagelVariant.Everything)); + Console.WriteLine("\nEverything bagel added!\n"); + break; + + case "4": + basket.AddItem(Inventory.CreateBagel(BagelVariant.Sesame)); + Console.WriteLine("\nSesame bagel added!\n"); + break; + } + Console.WriteLine("Would you like a filling with that? Y/N\n"); + var wouldYouInput = Console.ReadLine(); + switch (wouldYouInput.ToLower()) + { + case "y": + Console.WriteLine("\nWhat filling do you want?\n"); + Console.WriteLine("1. Bacon"); + Console.WriteLine("2. Egg"); + Console.WriteLine("3. Cheese"); + Console.WriteLine("4. Cream Cheese"); + Console.WriteLine("5. Smoked Salmon"); + Console.WriteLine("6. Ham\n"); + var fillingInput = Console.ReadLine(); + switch (fillingInput) + { + case "1": + basket.AddItem(Inventory.CreateFilling(FillingVariant.Bacon)); + Console.WriteLine("\nBacon added!\n"); + break; + + case "2": + basket.AddItem(Inventory.CreateFilling(FillingVariant.Egg)); + Console.WriteLine("\nEgg added!\n"); + break; + + case "3": + basket.AddItem(Inventory.CreateFilling(FillingVariant.Cheese)); + Console.WriteLine("\nCheese added!\n"); + break; + + case "4": + basket.AddItem(Inventory.CreateFilling(FillingVariant.CreamCheese)); + Console.WriteLine("\nCream Cheese added!\n"); + break; + + case "5": + basket.AddItem(Inventory.CreateFilling(FillingVariant.SmokedSalmon)); + Console.WriteLine("\nSmoked Salmon added!\n"); + break; + + case "6": + basket.AddItem(Inventory.CreateFilling(FillingVariant.Ham)); + Console.WriteLine("\nHam added!\n"); + break; + } + break; + + case "n": + break; + + default: + Console.WriteLine("Invalid input"); + break; + } + break; + + case "2": + Console.WriteLine("\nWhat coffee do you want?\n"); + Console.WriteLine("1. Black"); + Console.WriteLine("2. White"); + Console.WriteLine("3. Cappucino"); + Console.WriteLine("4. Latte\n"); + var coffeeInput = Console.ReadLine(); + switch (coffeeInput) + { + case "1": + basket.AddItem(Inventory.CreateCoffee(CoffeeVariant.Black)); + Console.WriteLine("\nBlack coffee added!\n"); + break; + + case "2": + basket.AddItem(Inventory.CreateCoffee(CoffeeVariant.White)); + Console.WriteLine("\nWhite coffee added!\n"); + break; + + case "3": + basket.AddItem(Inventory.CreateCoffee(CoffeeVariant.Cappucino)); + Console.WriteLine("\nCappucino added!\n"); + break; + + case "4": + basket.AddItem(Inventory.CreateCoffee(CoffeeVariant.Latte)); + Console.WriteLine("\nLatte added!\n"); + break; + } + break; + + case "3": + Console.WriteLine("Removing bagel UNFINISHED"); + //basket.RemoveItem(); + break; + + case "4": + Console.WriteLine("What should the new capacity be?\n"); + var newCapacity = int.Parse(Console.ReadLine()); + basket.ChangeCapacity(newCapacity); + Console.WriteLine($"The new capacity is:{newCapacity}\n"); + break; + + case "5": + Console.WriteLine("The current total cost is: "); + Console.WriteLine(checkout.TotalPrice(basket) + "\n"); + break; + + case "6": + + Console.WriteLine(checkout.Receipt(basket)); + Environment.Exit(0); + break; + + case "7": + Environment.Exit(0); + break; + + case "exit": + Environment.Exit(0); + break; + + default: + Console.WriteLine("\nInvalid input\n"); + break; + + } + } + } +} \ No newline at end of file diff --git a/exercise.main/TwilioText.cs b/exercise.main/TwilioText.cs new file mode 100644 index 00000000..a741e0d4 --- /dev/null +++ b/exercise.main/TwilioText.cs @@ -0,0 +1,33 @@ +using Microsoft.IdentityModel.Tokens; +using System; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Twilio; +using Twilio.Rest.Api.V2010.Account; +using Twilio.Types; + +namespace exercise.main +{ + class TwilioText + { + + private string accountSid = ""; + private string authToken = ""; + private string twilioNumber = "+18159494221"; + private string myNumber = "+1234567890"; + + public async Task SendOrder() + { + var messageContent = "Yello"; + + TwilioClient.Init(accountSid, authToken); + + var message = await MessageResource.CreateAsync( + body: messageContent, + from: new Twilio.Types.PhoneNumber(twilioNumber), + to: new Twilio.Types.PhoneNumber(myNumber)); + + Console.WriteLine(message.Body); + } + } +} diff --git a/exercise.main/exercise.main.csproj b/exercise.main/exercise.main.csproj index fd4bd08d..2e5676af 100644 --- a/exercise.main/exercise.main.csproj +++ b/exercise.main/exercise.main.csproj @@ -7,4 +7,8 @@ enable + + + + diff --git a/exercise.sln b/exercise.sln index 0efb5453..f9588aed 100644 --- a/exercise.sln +++ b/exercise.sln @@ -9,6 +9,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "exercise.tests", "exercise. EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{825CCFE7-4F2E-4770-8393-FEB732F66EE4}" ProjectSection(SolutionItems) = preProject + DomainModel.md = DomainModel.md extension1.md = extension1.md extension2.md = extension2.md extension3.md = extension3.md @@ -34,4 +35,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F839F4B4-D965-42CF-B457-007972706314} + EndGlobalSection EndGlobal diff --git a/exercise.tests/BasketTest.cs b/exercise.tests/BasketTest.cs new file mode 100644 index 00000000..6f3bbe68 --- /dev/null +++ b/exercise.tests/BasketTest.cs @@ -0,0 +1,79 @@ +using exercise.main; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.tests +{ + public class BasketTest + { + [Test] + public void AddBagelToBasketHasCorrectBagel() + { + var basket = new Basket(5); + var bagel = Inventory.CreateBagel(BagelVariant.Sesame); + + var added = basket.AddItem(bagel); + + Assert.IsTrue(added); + Assert.Contains(bagel, basket.Products); + } + + [Test] + public void RemoveBagelFromBasketNoLongerHasThatBagel() + { + var basket = new Basket(5); + var bagel = Inventory.CreateBagel(BagelVariant.Sesame); + + var added = basket.AddItem(bagel); + var removed = basket.RemoveItem(bagel); + + Assert.IsTrue(removed); + Assert.False(basket.Products.Contains(bagel)); + } + + [Test] + public void AddBagelPastCapacityThrowsError() + { + var basket = new Basket(2); + + basket.AddItem(Inventory.CreateBagel(BagelVariant.Plain)); + basket.AddItem(Inventory.CreateBagel(BagelVariant.Onion)); + + var added = basket.AddItem(Inventory.CreateBagel(BagelVariant.Everything)); + + Assert.IsFalse(added); + } + + [Test] + public void RemoveNonExistantBagelThrowsError() + { + var basket = new Basket(5); + var bagel = Inventory.CreateBagel(BagelVariant.Sesame); + + var removed = basket.RemoveItem(bagel); + + Assert.IsFalse(removed); + } + + [Test] + public void ChangeCapacityAllowsMoreItems() + { + var basket = new Basket(1); + + basket.AddItem(Inventory.CreateBagel(BagelVariant.Plain)); + var added = basket.AddItem(Inventory.CreateBagel(BagelVariant.Onion)); + Assert.IsFalse(added); + + basket.ChangeCapacity(2); + + added = basket.AddItem(Inventory.CreateCoffee(CoffeeVariant.Black)); + + Assert.IsTrue(added); + Assert.That(basket.Products.Count, Is.EqualTo(2)); + } + } +} diff --git a/exercise.tests/CheckoutTest.cs b/exercise.tests/CheckoutTest.cs new file mode 100644 index 00000000..444ce75a --- /dev/null +++ b/exercise.tests/CheckoutTest.cs @@ -0,0 +1,56 @@ +using System; +using NUnit.Framework; +using exercise.main; + +namespace exercise.tests +{ + public class CheckOutTest + { + [Test] + public void GetTotalCostIsSumOfAllPrices() + { + var basket = new Basket(10); + var checkout = new CheckOut(); + + basket.AddItem(Inventory.CreateBagel(BagelVariant.Plain)); + basket.AddItem(Inventory.CreateBagel(BagelVariant.Sesame)); + + var total = checkout.TotalPrice(basket); + + Assert.That(total, Is.EqualTo(0.88f)); + } + + [Test] + public void AddBagelNotInInventoryThrowsError() + { + var basket = new Basket(5); + var notInInventory = (BagelVariant)20; + + var error = Assert.Throws(() => Inventory.CreateBagel(notInInventory)); + + Assert.That(error.Message, Is.EqualTo("Bagel not in inventory")); + } + + [Test] + public void TotalPriceCalculatedWithSpecialDiscount() + { + var basket = new Basket(20); + + for (int i = 0; i < 6; i++) + { + basket.AddItem(Inventory.CreateBagel(BagelVariant.Onion)); + } + + for (int i = 0; i < 12; i++) + { + basket.AddItem(Inventory.CreateBagel(BagelVariant.Plain)); + } + + var checkout = new CheckOut(); + var total = checkout.TotalPrice(basket); + var receipt = checkout.Receipt(basket); + + Assert.That(total, Is.EqualTo(2.49f + 3.99f)); + } + } +} diff --git a/exercise.tests/ProductTest.cs b/exercise.tests/ProductTest.cs new file mode 100644 index 00000000..d682d109 --- /dev/null +++ b/exercise.tests/ProductTest.cs @@ -0,0 +1,36 @@ +using exercise.main; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.tests +{ + public class ProductTest + { + + [Test] + public void KnowTheCostOfBagel() + { + var bagel = Inventory.CreateBagel(BagelVariant.Onion); + + Assert.That(bagel.Price, Is.EqualTo(0.49f)); + } + + [Test] + public void CanAddDifferentTypesToBasket() + { + var basket = new Basket(5); + + var bagel = Inventory.CreateBagel(BagelVariant.Onion); + var filling = Inventory.CreateFilling(FillingVariant.Cheese); + + basket.AddItem(bagel); + basket.AddItem(filling); + + Assert.Contains(bagel, basket.Products); + Assert.Contains(filling, basket.Products); + } + } +} diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs deleted file mode 100644 index 7bdb8968..00000000 --- a/exercise.tests/UnitTest1.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace exercise.tests; - -public class Tests -{ - [SetUp] - public void Setup() - { - } - - [Test] - public void Test1() - { - Assert.Pass(); - } -} \ No newline at end of file diff --git a/exercise.tests/exercise.tests.csproj b/exercise.tests/exercise.tests.csproj index 9fed8e17..4be8063d 100644 --- a/exercise.tests/exercise.tests.csproj +++ b/exercise.tests/exercise.tests.csproj @@ -15,6 +15,7 @@ +