diff --git a/domain-model.md b/domain-model.md
new file mode 100644
index 00000000..f4ae26b5
--- /dev/null
+++ b/domain-model.md
@@ -0,0 +1,109 @@
+# Domain Modelling
+
+## Interfaces
+
+### IInventoryProduct
+
+| Property/Method | Scenario | Returns |
+| --------------- | --------------------------------------------------------- | ------- |
+| Name | Full item name | string |
+| SKU | Item code | string |
+| Price | Undiscounted base price (with no fillings) | Decimal |
+| GetFinalPrice() | Returns the final item price, with discounts and fillings | Decimal |
+
+### IDiscountable
+
+| Property / Methods | Scenario | Returns |
+| ------------------------------- | --------------------------------------------------------------- | ------- |
+| DiscountedPrice | price after discounts | Decimal |
+| IsDiscounted | is item discounted boolean | bool |
+| SetDiscountPrice(Decimal price) | sets the discount price | |
+| GetSavedAmount() | returns the difference between
original and discounted price | Decimal |
+
+### IFillable
+
+| Property / Method | Scenario | Returns |
+| --------------------------- | -------------------------------- | -------------- |
+| Fillings | stores all filling on an item | List\ |
+| AddFilling(Filling filling) | add a filling to a fillable item | |
+
+### IReceiptPrinter
+
+| Method | Scenario |
+| --------------------- | ------------------ |
+| Print(string receipt) | Prints the receipt |
+
+
+## Inheriting Classes
+
+### Bagel : IInventoryProduct, IDiscountable, IFillable
+### Coffee : IInventoryProduct, IDiscountable
+### Filling : IInventoryProduct
+
+### ConsoleReceiptPrinter : IReceiptPrinter
+### TwilioReceiptPrinter : IReceiptPrinter
+
+### BasketFullException : Exception
+### ItemNotInBasketException : Exception
+
+
+## Other classes
+
+### Basket
+
+| Property / method | Scenario | Returns |
+| ------------------------------------- | ---------------------------------------------------------------------- | --------------------- |
+| Capacity | shows basket's capacity | int |
+| Products | shows the content of the basket | List\ |
+| Remove(string SKU) | remove product in the basket | |
+| ChangeCapacity(int capacity) | Changes basket's capacity | |
+| GetTotalCost() | Get Final cost of all items in the basket | Decimal |
+| PrintReceipt(IReceiptPrinter printer) | prints receipt | |
+| private CalculateDiscounts() | calculates discounts when adding and removing products from the basket | |
+
+### Discount
+
+| Property / Method | Scenario | Returns |
+| ------------------------- | ------------------------------------------------------------------------------------------- | --------------------------- |
+| ItemRequirementAmountDict | stores requirements for a discount to be eligible,
in a format of | Dictionary |
+| ItemDiscountedPriceDict | stores discounted price for item, mapped to SKU | Dictionary |
+| SavedAmount | Amount the discount saved | Decimal |
+| CalculateSavedAmount() | Calculated saved amount
+
+
+## Static classes
+
+### Discounts
+
+| Property / Method | Scenario | Returns |
+| ----------------- | --------------------------------- | -------------- |
+| List | stores a list of active discounts | List |
+| BgloDiscount() | | Discount |
+| BglpDiscount() | | Discount |
+| BgleDiscount() | | Discount |
+| CofbDiscount() | | Discount |
+
+### Prices
+
+| Property | scenario | returns |
+| ------------- | ------------------------------------ | --------------------------- |
+| SkuToPriceMap | Dictionary mapping SKU to base price | Dictionary |
+
+### Products
+
+| Method | Returns |
+| ------ | ------- |
+| BGLO() | Bagel |
+| BGLP() | Bagel |
+| BGLE() | Bagel |
+| BGLS() | Bagel |
+| COFB() | Coffee |
+| COFW() | Coffee |
+| COFC() | Coffee |
+| COFL() | Coffee |
+| FILB() | Filling |
+| FILE() | Filling |
+| FILC() | Filling |
+| FILX() | Filling |
+| FILS() | Filling |
+| FILH() | Filling |
diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs
new file mode 100644
index 00000000..f26883a8
--- /dev/null
+++ b/exercise.main/Basket.cs
@@ -0,0 +1,183 @@
+using exercise.main.Collections;
+using exercise.main.Exceptions;
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main
+{
+ public class Basket
+ {
+ private int _capacity;
+ private List _products = new();
+
+
+ public List Products { get { return _products; } }
+ public int Capacity { get { return _capacity; } }
+
+ public Basket(int capacity)
+ {
+ _capacity = capacity;
+ }
+
+ public void Add(IDiscountable product)
+ {
+ if (product == null)
+ throw new ArgumentNullException();
+
+ if (_products.Count == _capacity)
+ throw new BasketFullException();
+
+ _products.Add(product);
+
+ CalculateDiscounts();
+ }
+
+ public void Remove(string SKU)
+ {
+ if (_products.Count == 0)
+ throw new ItemNotInBasketException();
+
+ if (_products.All(p => p.SKU != SKU))
+ throw new ItemNotInBasketException();
+
+ var itemToRemove = _products.Where(p => p.SKU == SKU).FirstOrDefault();
+ _products.Remove(itemToRemove);
+
+ CalculateDiscounts();
+ }
+
+ public void ChangeCapacity(int newCapacity)
+ {
+ _capacity = newCapacity;
+ }
+
+ public Decimal GetTotalCost()
+ {
+ return _products.Sum(p => p.GetFinalPrice());
+ }
+
+ private void CalculateDiscounts()
+ {
+ ResetDiscountedPrices();
+
+ var discounts = new Stack(Discounts.List.OrderBy(p => p.SavedAmount));
+
+ var undiscountedItems = _products.Select(p => p).ToList();
+ var discountedItems = new List();
+
+ while (discounts.Count > 0)
+ {
+ var discount = discounts.Peek();
+
+ bool missingRequirements = false;
+ foreach (var kvp in discount.ItemRequirementAmountDict)
+ {
+ var count = undiscountedItems.Count(k => k.SKU == kvp.Key);
+ if (count < kvp.Value)
+ {
+ discounts.Pop();
+ missingRequirements = true;
+ break;
+ }
+ }
+
+ if (missingRequirements)
+ continue;
+
+ // apply discounted prices if requirement passed
+ // move discounted items to new list
+
+ // todo refactor use 1 list with DiscountState enum instead of undiscounted and discounted lists?
+
+ foreach (var kvp in discount.ItemRequirementAmountDict)
+ {
+ var newPrice = discount.ItemDiscountedPriceDict.GetValueOrDefault(kvp.Key);
+ var discounted = undiscountedItems.Where(p => p.SKU == kvp.Key).Take(kvp.Value).ToList();
+ discounted.ForEach(p => p.SetDiscountPrice(newPrice));
+ discounted.ForEach(p => p.IsDiscounted = true);
+ discountedItems.AddRange(discounted);
+
+ var itemsToRemove = undiscountedItems.Where(p => p.SKU == kvp.Key).Take(kvp.Value).ToList();
+ itemsToRemove.ForEach(i => undiscountedItems.Remove(i));
+ }
+ }
+
+ List newProducts = new();
+ newProducts.AddRange(undiscountedItems);
+ newProducts.AddRange(discountedItems);
+ _products = newProducts;
+ }
+
+ private void ResetDiscountedPrices()
+ {
+ _products.ForEach(p => p.IsDiscounted = false);
+ }
+
+ public void PrintReceipt(IReceiptPrinter printer)
+ {
+ var groupedByItem = _products.GroupBy(p => p.SKU);
+
+ string combinedItemsString = "";
+
+ foreach (var group in groupedByItem)
+ {
+ var name = group.FirstOrDefault().Name;
+ var price = group.Sum(p => p.GetFinalPrice());
+ var itemString = $"{name}" + " " + $"{group.Count()}" + " " + $"{price.ToString("F2")}" + "\n";
+
+ combinedItemsString += itemString;
+
+ if (group.Any(p => p.IsDiscounted))
+ {
+ var savedAmount = group.Sum(p => p.GetSavedAmount());
+ var savedAmountString = $" (-{savedAmount.ToString("F2")})\n";
+ combinedItemsString += savedAmountString;
+ }
+
+
+ }
+
+ var combinedReceiptString = "";
+ combinedReceiptString += GetReceiptStartString();
+ combinedReceiptString += combinedItemsString;
+ combinedReceiptString += GetReceiptEndString();
+
+ printer.Print(combinedReceiptString);
+ }
+
+ private string GetReceiptStartString()
+ {
+ var startString = "";
+ startString += " ~~~ Bob's Bagels ~~~\n";
+ startString += "\n";
+ startString += $" {DateTime.Now}\n";
+ startString += "\n";
+ startString += "----------------------------\n";
+ startString += "\n";
+
+
+ return startString;
+ }
+
+ private string GetReceiptEndString()
+ {
+ var totalPrice = GetTotalCost();
+
+ var endString = "";
+ endString += "\n";
+ endString += "----------------------------\n";
+ endString += $"Total {totalPrice.ToString("F2")}\n";
+ endString += "\n";
+ endString += " Thank you\n";
+ endString += " For your order!\n";
+
+
+ return endString;
+ }
+ }
+}
diff --git a/exercise.main/Collections/Discounts.cs b/exercise.main/Collections/Discounts.cs
new file mode 100644
index 00000000..716cf91f
--- /dev/null
+++ b/exercise.main/Collections/Discounts.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Collections
+{
+ public static class Discounts
+ {
+ public static List List = new List {
+ {BgloDiscount()},
+ {BglpDiscount()},
+ {BgleDiscount()},
+ {CofbDiscount()},
+ };
+
+
+ public static Discount BgloDiscount()
+ {
+ return new Discount(
+ new Dictionary
+ {
+ {"BGLO", 6}
+ },
+ new Dictionary
+ {
+ {"BGLO", 0.415m}
+ }
+ );
+ }
+
+ public static Discount BglpDiscount()
+ {
+ return new Discount(
+ new Dictionary
+ {
+ {"BGLP", 12}
+ },
+ new Dictionary
+ {
+ {"BGLP", 0.3325m}
+ }
+ );
+ }
+
+ public static Discount BgleDiscount()
+ {
+ return new Discount(
+ new Dictionary
+ {
+ {"BGLE", 6}
+ },
+ new Dictionary
+ {
+ {"BGLE", 0.415m}
+ }
+ );
+ }
+
+ public static Discount CofbDiscount()
+ {
+ return new Discount(
+ new Dictionary
+ {
+ {"COFB", 1},
+ {"BGLP", 1}
+ },
+ new Dictionary
+ {
+ {"COFB", 0.99m},
+ {"BGLP", 0.26m}
+ }
+ );
+ }
+ }
+}
diff --git a/exercise.main/Collections/Prices.cs b/exercise.main/Collections/Prices.cs
new file mode 100644
index 00000000..007e59b4
--- /dev/null
+++ b/exercise.main/Collections/Prices.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Collections
+{
+ public static class Prices
+ {
+ public static Dictionary SkuToPriceMap = new Dictionary
+ {
+ {"BGLO", 0.49m},
+ {"BGLP", 0.39m},
+ {"BGLE", 0.49m},
+ {"BGLS", 0.49m},
+ {"COFB", 0.99m},
+ {"COFW", 1.19m},
+ {"COFC", 1.29m},
+ {"COFL", 1.29m},
+ {"FILB", 0.12m},
+ {"FILE", 0.12m},
+ {"FILC", 0.12m},
+ {"FILX", 0.12m},
+ {"FILS", 0.12m},
+ {"FILH", 0.12m},
+ };
+ }
+}
diff --git a/exercise.main/Collections/Products.cs b/exercise.main/Collections/Products.cs
new file mode 100644
index 00000000..1658f468
--- /dev/null
+++ b/exercise.main/Collections/Products.cs
@@ -0,0 +1,116 @@
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using exercise.main.Items;
+
+namespace exercise.main.Collections
+{
+ public static class Products
+ {
+ /*
+BGLO 0.49 Bagel Onion
+BGLP 0.39 Bagel Plain
+BGLE 0.49 Bagel Everything
+BGLS 0.49 Bagel Sesame
+
+COFB 0.99 Coffee Black
+COFW 1.19 Coffee White
+COFC 1.29 Coffee Capuccino
+COFL 1.29 Coffee Latte
+
+FILB 0.12 Filling Bacon
+FILE 0.12 Filling Egg
+FILC 0.12 Filling Cheese
+FILX 0.12 Filling Cream Cheese
+FILS 0.12 Filling Smoked Salmon
+FILH 0.12 Filling Ham
+ */
+
+ public static Bagel BGLO()
+ {
+ var sku = "BGLO";
+ return new Bagel("Onion Bagel" ,sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Bagel BGLP()
+ {
+ var sku = "BGLP";
+ return new Bagel("Plain Bagel", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Bagel BGLE()
+ {
+ var sku = "BGLE";
+ return new Bagel("Everything Bagel", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Bagel BGLS()
+ {
+ var sku = "BGLS";
+ return new Bagel("Sesame Bagel", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Coffee COFB()
+ {
+ var sku = "COFB";
+ return new Coffee("Black Coffee", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Coffee COFW()
+ {
+ var sku = "COFW";
+ return new Coffee("White Coffee", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Coffee COFC()
+ {
+ var sku = "COFC";
+ return new Coffee("Capuccino", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Coffee COFL()
+ {
+ var sku = "COFL";
+ return new Coffee("Latte", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILB()
+ {
+ var sku = "FILB";
+ return new Filling("Bacon", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILE()
+ {
+ var sku = "FILE";
+ return new Filling("Egg", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILC()
+ {
+ var sku = "FILC";
+ return new Filling("Cheese", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILX()
+ {
+ var sku = "FILX";
+ return new Filling("Cream Cheese", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILS()
+ {
+ var sku = "FILS";
+ return new Filling("Smoked Salmon", sku, Prices.SkuToPriceMap[sku]);
+ }
+
+ public static Filling FILH()
+ {
+ var sku = "FILH";
+ return new Filling("Ham", sku, Prices.SkuToPriceMap[sku]);
+ }
+ }
+}
diff --git a/exercise.main/Discount.cs b/exercise.main/Discount.cs
new file mode 100644
index 00000000..86aff8a3
--- /dev/null
+++ b/exercise.main/Discount.cs
@@ -0,0 +1,78 @@
+using exercise.main.Collections;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main
+{
+ public class Discount
+ {
+ public Dictionary ItemRequirementAmountDict;
+ public Dictionary ItemDiscountedPriceDict;
+ public Decimal SavedAmount;
+ public Decimal SavingsPercentage;
+ public Decimal NewTotalPrice;
+
+ public Discount(Dictionary itemRequirementAmountDict, Dictionary itemDiscountedPriceDict)
+ {
+ ItemRequirementAmountDict = itemRequirementAmountDict;
+ ItemDiscountedPriceDict = itemDiscountedPriceDict;
+
+ SavedAmount = CalculateSavedAmount();
+ SavingsPercentage = CalculatePercentageSavedAmount();
+ NewTotalPrice = CalculateNewTotal();
+ }
+
+ private Decimal CalculateSavedAmount()
+ {
+ Decimal oldTotalCost = 0;
+ Decimal newTotalCost = 0;
+
+ foreach (var key in ItemRequirementAmountDict.Keys)
+ {
+ var oldPrice = Prices.SkuToPriceMap.GetValueOrDefault(key);
+ var newPrice = ItemDiscountedPriceDict.GetValueOrDefault(key);
+ var amount = ItemRequirementAmountDict.GetValueOrDefault(key);
+
+ oldTotalCost += oldPrice * amount;
+ newTotalCost += newPrice * amount;
+ }
+
+ return oldTotalCost - newTotalCost;
+ }
+
+ private Decimal CalculatePercentageSavedAmount()
+ {
+ Decimal oldTotalCost = 0;
+ Decimal newTotalCost = 0;
+
+ foreach (var key in ItemRequirementAmountDict.Keys)
+ {
+ var oldPrice = Prices.SkuToPriceMap.GetValueOrDefault(key);
+ var newPrice = ItemDiscountedPriceDict.GetValueOrDefault(key);
+ var amount = ItemRequirementAmountDict.GetValueOrDefault(key);
+
+ oldTotalCost += oldPrice * amount;
+ newTotalCost += newPrice * amount;
+ }
+
+ return 1 - (oldTotalCost / newTotalCost);
+ }
+
+ private Decimal CalculateNewTotal()
+ {
+ Decimal newTotalCost = 0;
+
+ foreach (var key in ItemRequirementAmountDict.Keys)
+ {
+ var newPrice = ItemDiscountedPriceDict.GetValueOrDefault(key);
+ var amount = ItemRequirementAmountDict.GetValueOrDefault(key);
+ newTotalCost += newPrice * amount;
+ }
+
+ return newTotalCost;
+ }
+ }
+}
diff --git a/exercise.main/Exceptions/BasketFullException.cs b/exercise.main/Exceptions/BasketFullException.cs
new file mode 100644
index 00000000..f2fdcb45
--- /dev/null
+++ b/exercise.main/Exceptions/BasketFullException.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Exceptions
+{
+ public class BasketFullException : Exception
+ {
+ }
+}
diff --git a/exercise.main/Exceptions/ItemNotInBasketException.cs b/exercise.main/Exceptions/ItemNotInBasketException.cs
new file mode 100644
index 00000000..4842e0e2
--- /dev/null
+++ b/exercise.main/Exceptions/ItemNotInBasketException.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Exceptions
+{
+ public class ItemNotInBasketException : Exception
+ {
+ }
+}
diff --git a/exercise.main/Interfaces/IDiscountable.cs b/exercise.main/Interfaces/IDiscountable.cs
new file mode 100644
index 00000000..232a9403
--- /dev/null
+++ b/exercise.main/Interfaces/IDiscountable.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Interfaces
+{
+ public interface IDiscountable : IInventoryProduct
+ {
+ public Decimal DiscountedPrice { get; }
+ public bool IsDiscounted { get; set; }
+
+ public void SetDiscountPrice(Decimal discountPrice);
+
+ public Decimal GetSavedAmount();
+ }
+}
diff --git a/exercise.main/Interfaces/IFillable.cs b/exercise.main/Interfaces/IFillable.cs
new file mode 100644
index 00000000..7885e145
--- /dev/null
+++ b/exercise.main/Interfaces/IFillable.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using exercise.main.Items;
+
+namespace exercise.main.Interfaces
+{
+ public interface IFillable
+ {
+ public List Fillings { get; }
+
+ public void AddFillings(Filling filling)
+ {
+
+ }
+ }
+}
diff --git a/exercise.main/Interfaces/IInventoryProduct.cs b/exercise.main/Interfaces/IInventoryProduct.cs
new file mode 100644
index 00000000..6604cf35
--- /dev/null
+++ b/exercise.main/Interfaces/IInventoryProduct.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Interfaces
+{
+ public interface IInventoryProduct
+ {
+ public string Name { get; }
+ public string SKU { get; }
+ public Decimal Price { get; }
+
+ public Decimal GetFinalPrice();
+
+ }
+}
diff --git a/exercise.main/Interfaces/IReceiptPrinter.cs b/exercise.main/Interfaces/IReceiptPrinter.cs
new file mode 100644
index 00000000..ae998c5b
--- /dev/null
+++ b/exercise.main/Interfaces/IReceiptPrinter.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Interfaces
+{
+ public interface IReceiptPrinter
+ {
+ public void Print(string receipt);
+ }
+}
diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs
new file mode 100644
index 00000000..ba870eb5
--- /dev/null
+++ b/exercise.main/Items/Bagel.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using exercise.main.Exceptions;
+using exercise.main.Interfaces;
+
+namespace exercise.main.Items
+{
+ public class Bagel : IInventoryProduct, IFillable, IDiscountable
+ {
+ private string _name;
+ private string _sku;
+ private Decimal _price;
+ private Decimal _discountedPrice;
+ private List _fillings = new();
+
+ public string SKU { get { return _sku; } }
+
+ public Decimal Price { get { return _price; } }
+
+ public Decimal DiscountedPrice { get { return _discountedPrice; } }
+
+ List IFillable.Fillings { get { return _fillings; } }
+
+ public bool IsDiscounted { get; set; }
+
+ public string Name { get { return _name; } }
+
+ public void SetDiscountPrice(Decimal discountPrice)
+ {
+ _discountedPrice = discountPrice;
+ }
+
+ public Decimal GetFinalPrice()
+ {
+ var sumFillings = _fillings.Sum(f => f.GetFinalPrice());
+ if (IsDiscounted)
+ return sumFillings + DiscountedPrice;
+
+ return sumFillings + _price;
+ }
+
+ public void AddFillings(Filling filling)
+ {
+ _fillings.Add(filling);
+ }
+
+ public decimal GetSavedAmount()
+ {
+ if (!IsDiscounted)
+ return 0;
+
+ var sumFillings = _fillings.Sum(f => f.GetFinalPrice());
+ var fullPrice = _price + sumFillings;
+
+ var discountedPrice = GetFinalPrice();
+
+ return fullPrice - discountedPrice;
+ }
+
+ public Bagel(string name, string sku, Decimal price) {
+ _name = name;
+ _sku = sku;
+ _price = price;
+ IsDiscounted = false;
+ }
+ }
+}
diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs
new file mode 100644
index 00000000..01240f5f
--- /dev/null
+++ b/exercise.main/Items/Coffee.cs
@@ -0,0 +1,55 @@
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Items
+{
+ public class Coffee : IInventoryProduct, IDiscountable
+ {
+ private string _name;
+ private string _sku;
+ private Decimal _price;
+ private Decimal _discountedPrice;
+
+ public string SKU { get { return _sku; } }
+
+ public Decimal Price { get { return _price; } }
+
+ public Decimal DiscountedPrice { get { return _discountedPrice; } }
+
+ public bool IsDiscounted { get; set; }
+
+ public string Name { get { return _name; } }
+
+ public Decimal GetFinalPrice()
+ {
+ if (IsDiscounted)
+ return _discountedPrice;
+
+ return _price;
+ }
+
+ public void SetDiscountPrice(Decimal discountPrice)
+ {
+ _discountedPrice = discountPrice;
+ }
+
+ public decimal GetSavedAmount()
+ {
+ if (!IsDiscounted)
+ return 0;
+
+ return _price - _discountedPrice;
+ }
+
+ public Coffee(string name, string sku, Decimal price)
+ {
+ _name = name;
+ _sku = sku;
+ _price = price;
+ }
+ }
+}
diff --git a/exercise.main/Items/Filling.cs b/exercise.main/Items/Filling.cs
new file mode 100644
index 00000000..cc8cd7b9
--- /dev/null
+++ b/exercise.main/Items/Filling.cs
@@ -0,0 +1,34 @@
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.Items
+{
+ public class Filling : IInventoryProduct
+ {
+ private string _name;
+ private string _sku;
+ private Decimal _price;
+
+ public string SKU { get { return _sku; } }
+
+ public Decimal Price { get { return _price; } }
+
+ public string Name => throw new NotImplementedException();
+
+ public Decimal GetFinalPrice()
+ {
+ return _price;
+ }
+
+ public Filling(string name, string sku, Decimal price)
+ {
+ _name = name;
+ _sku = sku;
+ _price = price;
+ }
+ }
+}
diff --git a/exercise.main/Program.cs b/exercise.main/Program.cs
index 3751555c..acc08c09 100644
--- a/exercise.main/Program.cs
+++ b/exercise.main/Program.cs
@@ -1,2 +1,33 @@
// See https://aka.ms/new-console-template for more information
-Console.WriteLine("Hello, World!");
+using exercise.main;
+using exercise.main.Collections;
+using exercise.main.ReceiptPrinters;
+
+var basket = new Basket(25);
+
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+basket.Add(Products.BGLP());
+
+basket.Add(Products.COFB());
+
+basket.Add(Products.BGLO());
+basket.Add(Products.BGLO());
+basket.Add(Products.BGLO());
+basket.Add(Products.BGLO());
+basket.Add(Products.BGLO());
+basket.Add(Products.BGLO());
+
+var price = basket.GetTotalCost();
+
+var printer = new ConsoleReceiptPrinter();
+basket.PrintReceipt(printer);
diff --git a/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs b/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs
new file mode 100644
index 00000000..5980b4fb
--- /dev/null
+++ b/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs
@@ -0,0 +1,17 @@
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace exercise.main.ReceiptPrinters
+{
+ public class ConsoleReceiptPrinter : IReceiptPrinter
+ {
+ public void Print(string receipt)
+ {
+ Console.WriteLine(receipt);
+ }
+ }
+}
diff --git a/exercise.main/ReceiptPrinters/TwilioReceiptPrinter.cs b/exercise.main/ReceiptPrinters/TwilioReceiptPrinter.cs
new file mode 100644
index 00000000..8c1e820c
--- /dev/null
+++ b/exercise.main/ReceiptPrinters/TwilioReceiptPrinter.cs
@@ -0,0 +1,31 @@
+using exercise.main.Interfaces;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+using Twilio;
+using Twilio.Rest.Api.V2010.Account;
+
+namespace exercise.main.ReceiptPrinters
+{
+ public class TwilioReceiptPrinter : IReceiptPrinter
+ {
+ string accountSid = Environment.GetEnvironmentVariable("TWILIO_ACCOUNT_SID");
+ string authToken = Environment.GetEnvironmentVariable("TWILIO_AUTH_TOKEN");
+
+ public TwilioReceiptPrinter()
+ {
+ TwilioClient.Init(accountSid, authToken);
+ }
+
+ public async void Print(string receipt)
+ {
+ var message = await MessageResource.CreateAsync(
+ body: receipt,
+ from: new Twilio.Types.PhoneNumber("+15017122661"),
+ to: new Twilio.Types.PhoneNumber("+15558675310"));
+ }
+ }
+}
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.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs
index 7bdb8968..543d1271 100644
--- a/exercise.tests/UnitTest1.cs
+++ b/exercise.tests/UnitTest1.cs
@@ -1,15 +1,245 @@
+using exercise.main;
+using exercise.main.Collections;
+using exercise.main.Exceptions;
+using exercise.main.Items;
+
namespace exercise.tests;
public class Tests
{
- [SetUp]
- public void Setup()
+ [Test]
+ public void AddItemToBasketTask()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+
+ var basketProducts = basket.Products.ToList();
+
+ Assert.That(basketProducts.Count, Is.EqualTo(1));
+ Assert.That(basketProducts.FirstOrDefault().SKU, Is.EqualTo("BGLO"));
+ }
+
+ [Test]
+ public void Add3ItemsToBasketTask()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ var basketProducts = basket.Products.ToList();
+
+ Assert.That(basketProducts.Count, Is.EqualTo(3));
+ Assert.That(basketProducts.FirstOrDefault().SKU, Is.EqualTo("BGLO"));
+ }
+
+ [Test]
+ public void RemoveItemFromBasketTask()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+
+ basket.Remove("BGLO");
+
+ var basketProducts = basket.Products.ToList();
+
+ Assert.That(basketProducts.Count, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void RemoveItemFromBasketTask2()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLP());
+
+ basket.Remove("BGLO");
+
+ var basketProducts = basket.Products.ToList();
+
+ Assert.That(basketProducts.Count, Is.EqualTo(1));
+ Assert.That(basketProducts.FirstOrDefault().SKU, Is.EqualTo("BGLP"));
+ }
+
+ [Test]
+ public void RemoveItemNotInBasketFromBasketTask()
+ {
+ var basket = new Basket(3);
+
+ var basketProducts = basket.Products.ToList();
+
+ Assert.Throws(() => basket.Remove("BGLO"));
+ }
+
+ [Test]
+ public void OverfillBasketGetExceptionTask()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ var bagel = Products.BGLO();
+
+ Assert.Throws(() => basket.Add(bagel));
+ }
+
+ [Test]
+ public void ChangeBasketCapacityTask()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ basket.ChangeCapacity(4);
+
+ basket.Add(Products.BGLO());
+
+ var basketProducts = basket.Products.ToList();
+ Assert.That(basketProducts.Count, Is.EqualTo(4));
+ }
+
+ [Test]
+ public void CheckBasketPriceTest()
+ {
+ var basket = new Basket(3);
+ basket.Add(Products.BGLO());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Products.BGLO().Price));
+ }
+
+ [Test]
+ public void CheckBasketPriceTest2()
{
+ var basket = new Basket(6);
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLP());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Products.BGLO().Price + Products.BGLP().Price));
}
+
[Test]
- public void Test1()
+ public void AddFillingToBagelTest()
{
- Assert.Pass();
+ var basket = new Basket(6);
+
+ var bagel = Products.BGLO();
+ var filling = Products.FILS();
+ bagel.AddFillings(filling);
+
+ basket.Add(bagel);
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Products.BGLO().Price + Products.FILS().Price));
+ }
+
+ [Test]
+ public void AddBGLODiscountTest()
+ {
+ var basket = new Basket(6);
+
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Discounts.BgloDiscount().NewTotalPrice));
+ }
+
+ [Test]
+ public void AddBGLODiscountTest2()
+ {
+ var basket = new Basket(7);
+
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ basket.Add(Products.BGLO());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Discounts.BgloDiscount().NewTotalPrice + Products.BGLO().Price));
+ }
+
+ [Test]
+ public void AddBGLOThenRemoveDiscountTest()
+ {
+ var basket = new Basket(6);
+
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+ basket.Add(Products.BGLO());
+
+ basket.Remove("BGLO");
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Products.BGLO().Price * 5));
+ }
+
+ [Test]
+ public void AddBGLPDiscountTest()
+ {
+ var basket = new Basket(15);
+
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Discounts.BglpDiscount().NewTotalPrice));
+ }
+
+ [Test]
+ public void AddBGLPDiscountThenCoffeeTest()
+ {
+ var basket = new Basket(15);
+
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+ basket.Add(Products.BGLP());
+
+ basket.Add(Products.COFB());
+
+ var price = basket.GetTotalCost();
+
+ Assert.That(price, Is.EqualTo(Discounts.BglpDiscount().NewTotalPrice + Products.COFB().Price));
}
}
\ 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 @@
+
+
+
+