From 9879ec07d359fadfbab4289a3b4188a8e7bca305 Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Tue, 14 Jan 2025 21:43:15 +0100 Subject: [PATCH 1/3] Finished exercise + 2 extensions. --- Domain-model.md | 20 ++ README.md | 16 ++ exercise.main/Basket.cs | 339 +++++++++++++++++++++++++++ exercise.main/Inventory.cs | 42 ++++ exercise.main/Item.cs | 27 +++ exercise.main/Receipts.cs | 12 + exercise.sln | 1 + exercise.tests/UnitTest1.cs | 232 +++++++++++++++++- exercise.tests/exercise.tests.csproj | 4 + 9 files changed, 683 insertions(+), 10 deletions(-) create mode 100644 Domain-model.md create mode 100644 exercise.main/Basket.cs create mode 100644 exercise.main/Inventory.cs create mode 100644 exercise.main/Item.cs create mode 100644 exercise.main/Receipts.cs diff --git a/Domain-model.md b/Domain-model.md new file mode 100644 index 00000000..bf2f5075 --- /dev/null +++ b/Domain-model.md @@ -0,0 +1,20 @@ + +## Domain Model + +| Classes | Methods/Properties | Scenario | Outputs | +|---------|--------------------------------------------|---------------------------------------------------------------|-----------------------------| +|Basket.cs| AddItem(Item item) | Add type of bagel | | +|Basket.cs| RemoveItem(Item item) | Remove bagel | | +|Basket.cs| Public property FullBasket(string message) | Message if basket full | String "Basket is full" | +|Basket.cs| ChangeCapacityForBaskets(int NewCapacity) | Change capacity of baskets | | +|Basket.cs| CanRemoveItemFromBasket | Get message if trying to remove item that isn`t in the basket | String "Item not in basket" | +|Basket.cs| CalculateTotalPriceWithDiscount() | Get the total cost of items in basket | Int showing totalCost | +|Basket.cs| BagelCost(int priceBagel) | Get the cost of the bagel before adding to basket | Int showing cost of bagel | +|Basket.cs| AddItem() | Choose filling before adding it to bagel order | | +| | ShowCostForFilling() | Get the cost for filling before adding it | Int showing fillingCost | +| | CheckIfInInventory() | Can only order things that are stocked in inventory | String "Not in inventory" | +| | CalculateTotalPriceWithDiscount() | Prices will change based on how many items | | +| | GenerateReceipt() | Generate receipt for each order | String containing receipt | + + + diff --git a/README.md b/README.md index 4b229491..7f1223c0 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,22 @@ 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 the manager, +So we can earn more money, +I want customers to get special offers if they order more than one thing. +``` + +``` +12. +As a customer, +So I am sure I haven`t been charge too much, +I want to be able to receive a receipt for my buy. +``` + + + ## Bob's Inventory | SKU | Price | Name | Variant | diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs new file mode 100644 index 00000000..d5129f98 --- /dev/null +++ b/exercise.main/Basket.cs @@ -0,0 +1,339 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Basket + { + + private List BasketList; + public int BasketCapacity = 10; + + public Basket(int capacity) + { + BasketCapacity = capacity; + BasketList = new List(); + } + + public string AddItem(Item item) + + { + if (BasketList.Count >= BasketCapacity) + { + return "Cannot add the item because the basket is full"; + + } + else + BasketList.Add(item); + return "The item was successfully added"; + } + + public bool RemoveItem(Item item) + { + if (BasketList.Contains(item)) + { + BasketList.Remove(item); + return true; + } + else + { + return false; + } + } + + public int ChangeCapacityForBaskets(int NewCapacity) + { + if (NewCapacity != BasketCapacity) + { + BasketCapacity = NewCapacity; + + + } + return BasketCapacity; + + } + + public decimal CalculateTotalPriceWithDiscount() + { + decimal totalPrice = 0M; + Dictionary itemCounts = new Dictionary(); + + // Item count + foreach (Item item in BasketList) + { + if (itemCounts.ContainsKey(item.Sku)) + itemCounts[item.Sku] += 1; + else + itemCounts[item.Sku] = 1; + } + + // This is getting pretty ugly, im sorry... + Dictionary remainingBagels = new Dictionary(); + foreach (var skuCount in itemCounts) + { + string sku = skuCount.Key; + int count = skuCount.Value; + + // Discount for Onion and Everything Bagels + if (sku == "BGLO") + { + int discountSets = count / 6; + int remainder = count % 6; + totalPrice += discountSets * 2.49M + remainder * 0.49M; + + // Storing remaining bagels for pairing + remainingBagels[sku] = remainder; + } + + else if (sku == "BGLE") + { + int discountSets = count / 6; + int remainder = count % 6; + totalPrice += discountSets * 2.49M + remainder * 0.49M; + + // Store the remaining bagels for pairing with coffee + remainingBagels[sku] = remainder; + } + + // Discount logic for Plain Bagels + else if (sku == "BGLP") + { + int discountSets = count / 12; + int remainder = count % 12; + totalPrice += discountSets * 3.99M + remainder * 0.39M; + + // Store the remaining bagels for pairing + remainingBagels[sku] = remainder; + } + else if (sku == "BGLS") + { + remainingBagels[sku] = count; + } + } + // Black coffee and bagel pairing for discount + if (itemCounts.ContainsKey("COFB")) + { + int coffeeCount = itemCounts["COFB"]; + int totalRemainingBagels = remainingBagels.Values.Sum(); + int availablePairs = Math.Min(totalRemainingBagels, coffeeCount); //The smallest number of the two will dictate how many pairs + + + // Apply discount for the eligible pairs + totalPrice += availablePairs * 1.25M; + int bagelsToPair = availablePairs; + + + foreach (var sku in remainingBagels.Keys.ToList()) + { + if (bagelsToPair <= 0) + break; + + int remainingCount = remainingBagels[sku]; + + if (remainingCount > 0) + { + int bagelsUsed = Math.Min(remainingCount, bagelsToPair); + decimal bagelPrice = BasketList.First(item => item.Sku == sku).Price; + remainingBagels[sku] -= bagelsUsed; + bagelsToPair = bagelsToPair - bagelsUsed; + totalPrice -= bagelPrice; + } + } + + // Calculate remaining coffee that is not paired + int remainingCoffee = coffeeCount - availablePairs; + totalPrice += remainingCoffee * 0.99M; + + } + + // Remaining items without discounts + foreach (var skuCount in itemCounts) + { + string sku = skuCount.Key; + int count = skuCount.Value; + + if (sku != "BGLO" && sku != "BGLE" && sku != "BGLP" && sku != "COFB") + { + totalPrice += count * BasketList.First(item => item.Sku == sku).Price; + + } + } + + return totalPrice; + + } + + + public string CanRemoveItemFromBasket(Item item) + { + + if (BasketList.Contains(item)) + { + return "Item can be removed"; + + } + else + { + return "Item is not in the basket, and therefore can`t be removed"; + + } + + } + + public decimal BagelCost(Item item) + + { + if (item.Name == "Bagel") + + { + return item.Price; + + } + + else + { + throw new ArgumentException("The item is not a bagel."); + } + + } + + public decimal CheckFillingPrice(Item item) + { + if (item.Name == "Filling") + + { + return item.Price; + } + else + { + throw new ArgumentException("The item is not a filling"); + } + + } + + public bool CheckIfInInventory(string sku) + { + foreach (Item item in Inventory.inventoryList) + { + if (item.Sku == sku) + { + return true; + } + + } + return false; + } + + // Support-method to get whats in the basket and calculate discount price for + + public int CountItemsBySku(string sku) + { + return BasketList.Count(item => item.Sku == sku); + } + + public void GenerateReceipt() + { + // Print the header of the receipt + Console.WriteLine("~~~ Bob's Bagels ~~~"); + Console.WriteLine(); + Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + Console.WriteLine("----------------------------"); + Console.WriteLine(); + + decimal totalCost = 0M; + decimal totalDiscount = 0M; + + // Loop through each item in the basket + Dictionary itemCounts = new Dictionary(); + foreach (Item item in BasketList) + { + if (itemCounts.ContainsKey(item.Sku)) + itemCounts[item.Sku]+=1; + else + itemCounts[item.Sku] = 1; + } + + // Print each item and its cost + foreach (var skuCount in itemCounts) + { + string sku = skuCount.Key; + int count = skuCount.Value; + decimal price = BasketList.First(item => item.Sku == sku).Price; + + if (sku == "BGLO") + { + int discountSets = count / 6; + int remainder = count % 6; + decimal cost = discountSets * 2.49M + remainder * 0.49M; + Console.WriteLine($"Onion Bagel {count} £{cost:F2}"); + totalCost += cost; + totalDiscount += discountSets * 0.69M; + } + + else if (sku == "BGLE") + { + int discountSets = count / 6; + int remainder = count % 6; + decimal cost = discountSets * 2.49M + remainder * 0.49M; + Console.WriteLine($"Everything Bagel {count} £{cost:F2}"); + + totalCost += cost; + totalDiscount += discountSets * 0.69M; + } + + else if (sku == "BGLP") + { + int discountSets = count / 12; // Division without rest + int remainder = count % 12; //Modulo to calculate remainder + decimal cost = discountSets * 3.99M + remainder * 0.39M; + Console.WriteLine($"Plain Bagel {count} £{cost:F2}"); + + totalCost += cost; + totalDiscount += discountSets * 0.69M; + } + else if (sku == "COFB") + { + int bagelCount = CountItemsBySku("BGLO") + CountItemsBySku("BGLE") + CountItemsBySku("BGLP"); + int coffeeCount = count; + int eligiblePairs = Math.Min(bagelCount, coffeeCount); + decimal cost = eligiblePairs * 1.25M + (coffeeCount - eligiblePairs) * 0.99M; + Console.WriteLine($"Coffee {count} £{cost:F2}"); + totalCost += cost; + } + else + { + decimal cost = count * price; + Console.WriteLine($"{sku} {count} £{cost:F2}"); + totalCost += cost; + } + } + + Console.WriteLine("----------------------------"); + Console.WriteLine($"Total £{totalCost:F2}"); + Console.WriteLine(); + Console.WriteLine($"You saved a total of £{totalDiscount:F2}"); + Console.WriteLine(" on this shop"); + Console.WriteLine(); + Console.WriteLine(" Thank you"); + Console.WriteLine(" for your order!"); + } + + public void PrintBasketList() + { + // Iterate through each item in BasketList and print it + foreach (var item in BasketList) + { + Console.WriteLine(item.Sku); + } + } + + + + + } +} diff --git a/exercise.main/Inventory.cs b/exercise.main/Inventory.cs new file mode 100644 index 00000000..3ad9cc71 --- /dev/null +++ b/exercise.main/Inventory.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Inventory + { + + public static List inventoryList = new List + + { + new Item("BGLO", 0.49M, "Bagel", "Onion"), + new Item("BGLP", 0.39M, "Bagel", "Plain"), + new Item("BGLE", 0.49M, "Bagel", "Everything"), + new Item("BGLS", 0.49M, "Bagel", "Sesame"), + new Item("FILB", 0.12M, "Filling", "Bacon"), + new Item("FILE", 0.12M, "Filling", "Egg"), + new Item("FILC", 0.12M, "Filling", "Cheese"), + new Item("FILX", 0.12M, "Filling", "Cream Cheese"), + new Item("FILS", 0.12M, "Filling", "Smoked Salmon"), + new Item("FILH", 0.12M, "Filling", "Ham"), + new Item("COFB", 0.99M, "Coffee", "Black"), + new Item("COFW", 1.19M, "Coffee", "White"), + new Item("COFC", 1.29M, "Coffee", "Capuccino"), + new Item("COFL", 1.29M, "Coffee", "Latte"), + + }; + + } + +} + + + + + + + diff --git a/exercise.main/Item.cs b/exercise.main/Item.cs new file mode 100644 index 00000000..9e99caa4 --- /dev/null +++ b/exercise.main/Item.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace exercise.main +{ + public class Item + { + + public string Sku { get;} + public decimal Price { get;} + public string Name { get;} + public string Variant { get;} + + public Item(string sku, decimal price, string name, string variant) + { + Sku = sku; + Price = price; + Name = name; + Variant = variant; + } + + } +} diff --git a/exercise.main/Receipts.cs b/exercise.main/Receipts.cs new file mode 100644 index 00000000..0726d84b --- /dev/null +++ b/exercise.main/Receipts.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class Receipts + { + } +} diff --git a/exercise.sln b/exercise.sln index 0efb5453..5eb68450 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..b18c4136 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -1,15 +1,227 @@ -namespace exercise.tests; +using exercise.main; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using NUnit.Framework; +using System.Runtime.CompilerServices; +using NUnit.Framework.Interfaces; -public class Tests +namespace exercise.tests { - [SetUp] - public void Setup() + [TestFixture] + public class Tests { - } - [Test] - public void Test1() - { - Assert.Pass(); + Basket basket; + private Item item1; + private Item item2; + private Item item3; + private Item item4; + private Item item5; + private Item item6; + private Item item7; + private Item item8; + + [SetUp] + + public void SetUp() + + { + // Arrange for all tests + + basket = new Basket(20); + item1 = new Item("BGLO", 0.49M, "Bagel", "Onion"); + item2 = new Item("BGLP", 0.39M, "Bagel", "Plain"); + item3 = new Item("BGLE", 0.49M, "Bagel", "Everything"); + item4 = new Item("BGLS", 0.49M, "Bagel", "Sesame"); + item5 = new Item("BGLS", 0.49M, "Bagel", "Sesame"); + item6 = new Item("BGLS", 0.49M, "Bagel", "Sesame"); + item7 = new Item("FILS", 0.12M, "Filling", "Smoked Salmon"); + item8 = new Item("COFB", 0.99M, "Coffee", "Black"); + + basket.AddItem(item1); + basket.AddItem(item2); + + // Console.WriteLine("heihei"); + } + + [Test] + public void AddAndRemoveItem() + { + //2 requirements - Adding and removing items from basket + + //Act + basket.ChangeCapacityForBaskets(3); + string addedMessage = basket.AddItem(item3); + bool removed = basket.RemoveItem(item1); + basket.AddItem(item2); + string basketFull = basket.AddItem(item4); + //string NotInInventory = basket.AddItem(new Item("BGLC", 0.50M, "Bagel", "Chicken")); + + //Assert + + Assert.That(addedMessage, Is.EqualTo("The item was successfully added")); + Assert.IsTrue(removed); + Assert.That(basketFull, Is.EqualTo("Cannot add the item because the basket is full")); + //Assert.That(NotInInventory, Is.EqualTo("This item does not exist in the inventory")); + } + + [Test] + + public void CheckIfInInventory() + { + //Requirement - Can`t order items not in inventory + + //Act + + string nonExistingSku = "NOTVALID"; + bool result = basket.CheckIfInInventory(nonExistingSku); + + //Assert + Assert.That(result, Is.False); + } + + [Test] + public void ChangeCapacity() + { + //Act + int changedCapacity = basket.ChangeCapacityForBaskets(15); + + //Assert + Assert.That(changedCapacity, Is.EqualTo(15)); + } + + + [Test] + public void CostBagel() + { + + + //Act + decimal cost = basket.BagelCost(item1); + + //Assert + Assert.That(cost, Is.EqualTo( 0.49M)); + } + + [Test] + public void CostFilling() + { + + //Act + decimal cost = basket.CheckFillingPrice(item7); + + //Assert + Assert.That(cost, Is.EqualTo(0.12M)); + + } + + [Test] + public void CanRemoveItemFromBasket() + { + //Act + string message = basket.CanRemoveItemFromBasket(item1); + string error_message = basket.CanRemoveItemFromBasket(item4); + + //Assert + Assert.That(message, Is.EqualTo("Item can be removed")); + Assert.That(error_message, Is.EqualTo("Item is not in the basket, and therefore can`t be removed")); + + + } + + + [Test] + public void CheckForDiscount() + { + //Arrange + basket.AddItem(item1); + basket.AddItem(item1); //Adding 5 Bagel Onion to have 6 for the discount + basket.AddItem(item1); + basket.AddItem(item1); + basket.AddItem(item1); + basket.AddItem(item2); //Adding plain bagel + basket.AddItem(item8); //Adding Coffee to get the pairing discount with plain bagel + + //Act + decimal totalPrice = basket.CalculateTotalPriceWithDiscount(); + basket.GenerateReceipt(); + basket.PrintBasketList(); + + + //Discount should be: + //6 Onion Bagel 2,49 + // Plain Bagel x 1 = 0.39 + // Everything Bagel x 1 = 0.49 + // Black coffee + Plain Bagel = 1.25 + // 2.49 + 0.78 + 1.25 = 4.62 + + //Assert + decimal expectedPriceWithDiscounts = 2.49M + 0.39M + 1.25M; + Assert.That(totalPrice, Is.EqualTo(expectedPriceWithDiscounts)); + } + + [Test] + public void CheckForPlainBagelDiscount() + { + //Arrange + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + basket.AddItem(item2); + + //Act + decimal totalPrice = basket.CalculateTotalPriceWithDiscount(); + basket.GenerateReceipt(); + basket.PrintBasketList(); + + + //Price should be: + + // Plain Bagel x 12 = 3.99 + // Bagel Onion x 1 = 0.49 + + + //Assert + decimal expectedPriceWithDiscounts = 3.99M + 0.49M; + Assert.That(totalPrice, Is.EqualTo(expectedPriceWithDiscounts)); + } + + [Test] + public void CountItemsInBasket() + + //Act + { + basket.AddItem(item4); + basket.AddItem(item5); + basket.AddItem(item6); + int count = basket.CountItemsBySku("BGLS"); + + //Assert + Assert.That(count, Is.EqualTo(3)); + } + + + [Test] + + public void CheckForReceipt() + { + //Assert + Assert.That( basket.GenerateReceipt, Is.Not.EqualTo(null)); + } + + + } -} \ No newline at end of file +} + + \ 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 @@ + + + + From af3828ae212eb320f4abc70c1fbc1658fbacdc1f Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Tue, 14 Jan 2025 21:47:22 +0100 Subject: [PATCH 2/3] Fixed Domain Model --- Domain-model.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Domain-model.md b/Domain-model.md index bf2f5075..03689f5d 100644 --- a/Domain-model.md +++ b/Domain-model.md @@ -8,13 +8,13 @@ |Basket.cs| Public property FullBasket(string message) | Message if basket full | String "Basket is full" | |Basket.cs| ChangeCapacityForBaskets(int NewCapacity) | Change capacity of baskets | | |Basket.cs| CanRemoveItemFromBasket | Get message if trying to remove item that isn`t in the basket | String "Item not in basket" | -|Basket.cs| CalculateTotalPriceWithDiscount() | Get the total cost of items in basket | Int showing totalCost | +|Basket.cs| CalculateTotalPriceWithDiscount() | Get the total cost of items in basket | Int showing totalPrice | |Basket.cs| BagelCost(int priceBagel) | Get the cost of the bagel before adding to basket | Int showing cost of bagel | |Basket.cs| AddItem() | Choose filling before adding it to bagel order | | -| | ShowCostForFilling() | Get the cost for filling before adding it | Int showing fillingCost | -| | CheckIfInInventory() | Can only order things that are stocked in inventory | String "Not in inventory" | -| | CalculateTotalPriceWithDiscount() | Prices will change based on how many items | | -| | GenerateReceipt() | Generate receipt for each order | String containing receipt | +|Basket.cs| ShowCostForFilling() | Get the cost for filling before adding it | Int showing fillingCost | +|Basket.cs| CheckIfInInventory() | Can only order things that are stocked in inventory | String "Not in inventory" | +|Basket.cs| CalculateTotalPriceWithDiscount() | Prices will change based on how many items | Int showing totalPrice | +|Basket.cs| GenerateReceipt() | Generate receipt for each order | Print of receipt | From 6bd8691666d1c835c36390a9e950b61b6b3d33dc Mon Sep 17 00:00:00 2001 From: Kristoffer Blucher Date: Wed, 15 Jan 2025 08:31:47 +0100 Subject: [PATCH 3/3] Removed unused classes and methods for debugging --- exercise.main/Basket.cs | 12 ------------ exercise.main/Receipts.cs | 12 ------------ exercise.tests/UnitTest1.cs | 4 ++-- 3 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 exercise.main/Receipts.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index d5129f98..946e9473 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -323,17 +323,5 @@ public void GenerateReceipt() Console.WriteLine(" for your order!"); } - public void PrintBasketList() - { - // Iterate through each item in BasketList and print it - foreach (var item in BasketList) - { - Console.WriteLine(item.Sku); - } - } - - - - } } diff --git a/exercise.main/Receipts.cs b/exercise.main/Receipts.cs deleted file mode 100644 index 0726d84b..00000000 --- a/exercise.main/Receipts.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace exercise.main -{ - public class Receipts - { - } -} diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index b18c4136..09d2ad9d 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -148,7 +148,7 @@ public void CheckForDiscount() //Act decimal totalPrice = basket.CalculateTotalPriceWithDiscount(); basket.GenerateReceipt(); - basket.PrintBasketList(); + //Discount should be: @@ -182,13 +182,13 @@ public void CheckForPlainBagelDiscount() //Act decimal totalPrice = basket.CalculateTotalPriceWithDiscount(); basket.GenerateReceipt(); - basket.PrintBasketList(); //Price should be: // Plain Bagel x 12 = 3.99 // Bagel Onion x 1 = 0.49 + // Total = 4.48 //Assert