diff --git a/domain-model.md b/domain-model.md new file mode 100644 index 00000000..b42fec4e --- /dev/null +++ b/domain-model.md @@ -0,0 +1,8 @@ +| User stories | Classes | Methods | Considerations | Output/Function | +|--------------|-----------|----------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| +| 1,3,7,10 | Customer | AddProduct(IProduct product) (1) | Exception handling for full basket (3) Exception handling for invalid product (10) Inform customer of the price before adding (7) | Add product to costumers basket | +| 2,5 | Customer | RemoveProduct(IProduct product) (2) | Exception handling for nonexistent item (5) | Remove product from customers basket | +| 6 | Customer | ComputeCost(List basket) (6) | | Compute the total cost of the items in the basket | +| 4 | Manager | ChangeCapacity(int capacity) (4) | | Change the capacity of baskets | +| 8,9 | Bagel | AddFilling(Bagel filling) (8) | Inform customer about the price of a filling (9) | Add fillings to a Bagel | +| | Basket | AddProduct(IProduct product) | | Add product to the basket instance | | | \ No newline at end of file diff --git a/exercise.main/Bagel.cs b/exercise.main/Bagel.cs new file mode 100644 index 00000000..39c50c69 --- /dev/null +++ b/exercise.main/Bagel.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Bagel : IProduct + { + public decimal Price { get; set; } + public string Name { get; set; } + public string Variant { get; set; } + + public decimal _fillingPrice { get; set; } = 0; + + public List fillings { get; set; } + + public Bagel(string SKU) + { + //Name = "Bagel"; + //Variant = variant; + //Price = _prices[variant]; + //_fillingPrice = 0; + var info = ProductCatalog.GetProductInfo(SKU); + Price = info.Price; + Name = info.Name; + Variant = info.Variant; + fillings = new List(); + + } + + public void AddFilling(Filling filling) + { + fillings.Add(filling); + //_fillingPrice += filling.Price; + //Price += filling.Price; + } + + public List GetFillings() { return fillings; } + } +} diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs new file mode 100644 index 00000000..3c53b043 --- /dev/null +++ b/exercise.main/Basket.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Basket + { + private List _basket { get; } + private List _bagelFillings { get; } = new List(); + public static int Capacity = 3; + + public Basket() + { + _basket = new List(); + } + + public void AddProduct(IProduct product) + { + if (_basket.Count() == Capacity) + { + throw new InvalidOperationException("Basket capacity full - you can not add more items"); + } + else + { + if (product is Bagel) + { + Bagel bagel = (Bagel)product; + bagel.GetFillings().ForEach(x => _bagelFillings.Add(x)); + } + _basket.Add(product); + } + } + + public void RemoveProduct(IProduct product) + { + //_basket.Remove(product); + if (_basket.Contains(product)) + { + _basket.Remove(product); + } + else + { + throw new InvalidOperationException("Product does not exist in basket"); + } + } + + public decimal ComputeCost() + { + //return _basket.Sum(x => x.Price) - this.ComputeDiscounts(); + var info = this.ComputeDiscounts(); + decimal total = info.Discounts + info.RemainingItems.Sum(x => x.Price) + _bagelFillings.Sum(x => x.Price); + return total; + } + + public (decimal Discounts, List RemainingItems) ComputeDiscounts() + { + List bagels = _basket.Where(x => x is Bagel).OrderBy(v => v.Price).ToList(); + List coffees = _basket.Where(x => x is Coffee).OrderBy(v => v.Price).ToList(); + + decimal sixBagels = 0m; + decimal twelveBagels = 0m; + decimal coffeeBagels = 0m; + + int remainingBagels = 0; + int mod = bagels.Count % 12; + + if (bagels.Count > 11) + { + // USe list List.RemoveRange() starting at zero + int val = bagels.Count - mod; + twelveBagels = val * 3.99m / (12m); + bagels.RemoveRange(0,val); + } + if (mod > 5) + { + sixBagels = 2.49m; + remainingBagels = mod - 6; + bagels.RemoveRange(0, 6); + } + else + { + remainingBagels = mod; + } + + foreach (IProduct coffee in coffees) + { + if (coffee.Variant == "Black" && remainingBagels > 0) + { + coffeeBagels += 1.25m; + remainingBagels--; + bagels.RemoveAt(0); + //coffees.Remove(coffee); + coffee.Price = 0m; + } + } + + decimal discounts = twelveBagels + sixBagels + coffeeBagels; + List remainingItems = bagels.Concat(coffees).ToList(); + + return (discounts, remainingItems); + } + + public string GetReceipt() + { + StringBuilder sb = new StringBuilder(); + Dictionary productCount = new(); + foreach (IProduct item in _basket) + { + if (productCount.ContainsKey(item.Variant)) + { + int val = productCount[item.Variant].count + 1; + decimal price = productCount[item.Variant].priceTotal; + productCount[item.Variant] = (item.Name, val, price+item.Price); + } + else + { + productCount[item.Variant] = (item.Name, 1, item.Price); + } + } + int totalWidth = 30; + string title = "~~~ Bob's Bagels ~~~"; + string time = DateTime.Now.ToString(); + string end = "Thank you for your order!"; + + sb.AppendFormat("{0," + (((totalWidth - title.Length) / 2) + title.Length) + "}", title); + sb.AppendLine(); + sb.AppendLine(); + sb.AppendFormat("{0," + (((totalWidth - time.Length) / 2) + time.Length) + "}", time); + sb.AppendLine(); + sb.AppendLine(); + sb.AppendLine(new String('-', 30)); + sb.AppendLine(); + //sb.AppendLine(); + + foreach (var item in productCount) + { + sb.AppendFormat("{0,-20}{1,-5}{2,-5}", item.Key, item.Value.count, item.Value.priceTotal); + sb.AppendLine(); + } + + sb.AppendLine(); + sb.AppendLine(new String('-', 30)); + sb.AppendLine(); + sb.AppendFormat("{0," + (((totalWidth - end.Length) / 2) + end.Length) + "}", end); + + return sb.ToString(); + } + + + public int Count() + { + return _basket.Count; + } + + public List GetBasket { get { return _basket; } } + } +} diff --git a/exercise.main/Coffee.cs b/exercise.main/Coffee.cs new file mode 100644 index 00000000..b70a2cad --- /dev/null +++ b/exercise.main/Coffee.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Coffee : IProduct + { + public decimal Price { get; set; } + public string Name { get; set; } + public string Variant { get; set; } + + public Coffee(string SKU) + { + var info = ProductCatalog.GetProductInfo(SKU); + Price = info.Price; + Name = info.Name; + Variant = info.Variant; + } + } +} diff --git a/exercise.main/Customer.cs b/exercise.main/Customer.cs new file mode 100644 index 00000000..9bc4a539 --- /dev/null +++ b/exercise.main/Customer.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Customer + { + private Basket _basket { get; set; } + + public Customer() + { + _basket = new Basket(); + } + + public void AddProduct(IProduct product) + { + _basket.AddProduct(product); + } + + public void RemoveProduct(IProduct product) + { + _basket.RemoveProduct(product); + } + + public decimal BasketCost() + { + return _basket.ComputeCost(); + } + + public string GetReceipt() + { + return _basket.GetReceipt(); + } + + public Basket GetBasket { get { return _basket; } } + } + + +} diff --git a/exercise.main/Filling.cs b/exercise.main/Filling.cs new file mode 100644 index 00000000..775e604b --- /dev/null +++ b/exercise.main/Filling.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Filling : IProduct + { + public decimal Price { get; set; } + public string Name { get; set; } + public string Variant { get; set; } + + public Filling(string SKU) + { + var info = ProductCatalog.GetProductInfo(SKU); + Price = info.Price; + Name = info.Name; + Variant = info.Variant; + } + } +} diff --git a/exercise.main/IProduct.cs b/exercise.main/IProduct.cs new file mode 100644 index 00000000..5bff776e --- /dev/null +++ b/exercise.main/IProduct.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + + public interface IProduct + { + decimal Price { get; set; } + string Name { get; set; } + string Variant { get; set; } + } +} diff --git a/exercise.main/Manager.cs b/exercise.main/Manager.cs new file mode 100644 index 00000000..29c9430b --- /dev/null +++ b/exercise.main/Manager.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Manager + { + public Manager() + { + } + + public void ChangeBasketCapacity(int newCapacity) + { + Basket.Capacity = newCapacity; + } + } +} diff --git a/exercise.main/ProductCatalog.cs b/exercise.main/ProductCatalog.cs new file mode 100644 index 00000000..f8009df7 --- /dev/null +++ b/exercise.main/ProductCatalog.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public static class ProductCatalog + { + private static readonly Dictionary _products = new() + { + { "BGLO", ("Bagel", 0.49m, "Onion") }, + { "BGLP", ("Bagel", 0.39m, "Plain") }, + { "BGLE", ("Bagel", 0.49m, "Everything") }, + { "BGLS", ("Bagel", 0.49m, "Sesame") }, + { "COFB", ("Coffee", 0.99m, "Black") }, + { "COFW", ("Coffee", 1.19m, "White") }, + { "COFC", ("Coffee", 1.29m, "Capuccino") }, + { "COFL", ("Coffee", 1.29m, "Latte") }, + { "FILB", ("Filling", 0.12m, "Bacon") }, + { "FILE", ("Filling", 0.12m, "Egg") }, + { "FILC", ("Filling", 0.12m, "Cheese") }, + { "FILX", ("Filling", 0.12m, "Cream Cheese") }, + { "FILS", ("Filling", 0.12m, "Smoked Salmon") }, + { "FILH", ("Filling", 0.12m, "Ham") } + }; + + public static (string Name, decimal Price, string Variant) GetProductInfo(string code) + { + if (_products.TryGetValue(code, out var product)) + { + return product; + } + throw new KeyNotFoundException($"Product code '{code}' not found"); + } + } +} diff --git a/exercise.sln b/exercise.sln index 0efb5453..132b6c01 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 + domain-model.md = domain-model.md extension1.md = extension1.md extension2.md = extension2.md extension3.md = extension3.md diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 7bdb8968..975fb059 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -1,15 +1,204 @@ +using exercise.main; +using System.Buffers.Text; + namespace exercise.tests; public class Tests { - [SetUp] - public void Setup() + //[SetUp] + //public void Setup() + //{ + //} + + [Test] + public void TestAddBagelToBasket() { + Bagel bagel = new Bagel("BGLO"); + Customer customer = new Customer(); + customer.AddProduct(bagel); + Assert.That(customer.GetBasket.Count() > 0); + Assert.Pass(); } [Test] - public void Test1() + public void TestRemoveBagelFromBasket() { + Bagel bagel = new Bagel("BGLO"); + Customer customer = new Customer(); + customer.AddProduct(bagel); + Assert.That(customer.GetBasket.Count() > 0); + customer.RemoveProduct(bagel); + Assert.That(customer.GetBasket.Count() < 1); Assert.Pass(); } + + [Test] + public void TestBasketCapacity() + { + Bagel bagel1 = new Bagel("BGLO"); + Bagel bagel2 = new Bagel("BGLP"); + Bagel bagel3 = new Bagel("BGLE"); + Bagel bagel4 = new Bagel("BGLS"); + Customer customer = new Customer(); + customer.AddProduct(bagel1); + customer.AddProduct(bagel2); + customer.AddProduct(bagel3); + //customer.AddProduct(bagel4); + Assert.Throws(() => customer.AddProduct(bagel4)); + //Assert.That(); + } + + [Test] + public void TestManagerCapacityChange() + { + Assert.That(Basket.Capacity == 3); + Manager manager = new Manager(); + manager.ChangeBasketCapacity(4); + Assert.That(Basket.Capacity == 4); + } + + [Test] + public void TestRemoveInvalidItem() + { + Customer customer = new Customer(); + Bagel bagel = new Bagel("BGLO"); + Assert.Throws(() => customer.RemoveProduct(bagel)); + + } + + [Test] + public void TestComputeBasketCost() + { + Customer customer = new Customer(); + Bagel bagel1 = new Bagel("BGLO"); + Bagel bagel2 = new Bagel("BGLP"); + Bagel bagel3 = new Bagel("BGLE"); + //Bagel bagel4 = new Bagel("BGLS"); + customer.AddProduct(bagel1); + customer.AddProduct(bagel2); + customer.AddProduct(bagel3); + //customer.AddProduct(bagel4); + decimal expectedPrice = 1.37m; + Assert.That(expectedPrice, Is.EqualTo(customer.BasketCost())); + } + + [Test] + public void TestBagelCostBeforeAdd() + { + Customer customer = new Customer(); + Bagel bagel = new Bagel("BGLO"); + decimal expectedBagelPrice = 0.49m; + Assert.That(expectedBagelPrice, Is.EqualTo(bagel.Price)); + } + + [Test] + public void TestAddFillingsToBagel() + { + Bagel bagel = new Bagel("BGLO"); + Filling bacon = new Filling("FILB"); + bagel.AddFilling(bacon); + Assert.That(bagel.fillings.Contains(bacon)); + } + + [Test] + public void TestFillingPrice() + { + Filling bacon = new Filling("FILB"); + Assert.That(bacon.Price, Is.EqualTo(0.12m)); + } + + [Test] + public void TestInvalidItem() + { + //Filling bacon = new Filling("FILA"); + + //Bagel bagel = new Bagel("BGLZ"); + + Assert.Throws(() => new Bagel("BGLZ")); + Assert.Throws(() => new Filling("AAAA")); + + } + + [Test] + public void TestSomething() + { + Customer customer = new Customer(); + Bagel bagel = new Bagel("BGLO"); + Filling bacon = new Filling("FILB"); + bagel.AddFilling(bacon); + customer.AddProduct(bagel); + Assert.That(customer.BasketCost(), Is.EqualTo(0.61m)); + + } + + [Test] + public void TestDiscounts() + { + Customer customer = new Customer(); + Manager manager = new Manager(); + manager.ChangeBasketCapacity(50); + for (int i = 0; i < 12; i++) + { + customer.AddProduct(new Bagel("BGLO")); + } + Assert.That(customer.BasketCost(), Is.EqualTo(3.99m)); + + for (int i = 0; i < 6; i++) + { + customer.AddProduct(new Bagel("BGLO")); + } + Assert.That(customer.BasketCost(), Is.EqualTo(6.48m)); + + Customer customer2 = new Customer(); + for (int i = 0; i < 12; i++) + { + customer2.AddProduct(new Bagel("BGLS")); + } + Bagel bagel = new Bagel("BGLP"); + bagel.AddFilling(new Filling("FILB")); + customer2.AddProduct(bagel); + customer2.AddProduct(new Bagel("BGLP")); + Console.WriteLine(customer2.BasketCost()); + + Assert.That(customer2.BasketCost(), Is.EqualTo(5.09m)); + + customer2.AddProduct(new Coffee("COFB")); + + Assert.That(customer2.BasketCost(), Is.EqualTo(5.85m)); + + for (int i = 0; i < 6; i++) + { + customer2.AddProduct(new Bagel("BGLS")); + } + Assert.That(customer2.BasketCost(), Is.EqualTo(8.34m)); + + manager.ChangeBasketCapacity(3); + } + + [Test] + public void TestReceipt() + { + Customer customer = new Customer(); + Manager manager = new Manager(); + manager.ChangeBasketCapacity(50); + for (int i = 0; i < 2; i++) + { + customer.AddProduct(new Bagel("BGLO")); + } + for (int i = 0; i < 12; i++) + { + customer.AddProduct(new Bagel("BGLP")); + } + for (int i = 0; i < 6; i++) + { + customer.AddProduct(new Bagel("BGLE")); + } + for (int i = 0; i < 3; i++) + { + customer.AddProduct(new Coffee("COFB")); + } + + Console.WriteLine(customer.GetReceipt()); + } + } \ No newline at end of file diff --git a/exercise.tests/exercise.tests.csproj b/exercise.tests/exercise.tests.csproj index 9fed8e17..a3a97d4f 100644 --- a/exercise.tests/exercise.tests.csproj +++ b/exercise.tests/exercise.tests.csproj @@ -17,4 +17,8 @@ + + + +