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 @@ + + + +