From 14d656caa8179e88e78a5c6c67c5f55760fcdb74 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 11:05:30 +0200 Subject: [PATCH 01/11] feat: initial class signatures --- exercise.main/Basket.cs | 42 ++++++++ .../Exceptions/BasketFullException.cs | 12 +++ .../Exceptions/ItemNotInBasketException.cs | 12 +++ exercise.main/Interfaces/IDiscountable.cs | 15 +++ exercise.main/Interfaces/IFillable.cs | 18 ++++ exercise.main/Interfaces/IInventoryProduct.cs | 17 +++ exercise.main/InventoryProduct.cs | 32 ++++++ exercise.main/Items/Bagel.cs | 46 ++++++++ exercise.main/Items/Coffee.cs | 37 +++++++ exercise.main/Items/Filling.cs | 30 ++++++ exercise.main/Products.cs | 102 ++++++++++++++++++ exercise.tests/UnitTest1.cs | 94 +++++++++++++++- 12 files changed, 453 insertions(+), 4 deletions(-) create mode 100644 exercise.main/Basket.cs create mode 100644 exercise.main/Exceptions/BasketFullException.cs create mode 100644 exercise.main/Exceptions/ItemNotInBasketException.cs create mode 100644 exercise.main/Interfaces/IDiscountable.cs create mode 100644 exercise.main/Interfaces/IFillable.cs create mode 100644 exercise.main/Interfaces/IInventoryProduct.cs create mode 100644 exercise.main/InventoryProduct.cs create mode 100644 exercise.main/Items/Bagel.cs create mode 100644 exercise.main/Items/Coffee.cs create mode 100644 exercise.main/Items/Filling.cs create mode 100644 exercise.main/Products.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs new file mode 100644 index 00000000..aba211d1 --- /dev/null +++ b/exercise.main/Basket.cs @@ -0,0 +1,42 @@ +using exercise.main.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +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(IInventoryProduct product) + { + if (product == null) + throw new ArgumentNullException(); + + + } + + public void Remove(string SKU) + { + + } + + public void ChangeCapacity(int newCapacity) + { + _capacity = newCapacity; + } + } +} 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..0ebbba09 --- /dev/null +++ b/exercise.main/Interfaces/IDiscountable.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main.Interfaces +{ + public interface IDiscountable + { + public float DiscountedPrice { get; } + + public void SetDiscountPrice(float discountPrice); + } +} diff --git a/exercise.main/Interfaces/IFillable.cs b/exercise.main/Interfaces/IFillable.cs new file mode 100644 index 00000000..052ee001 --- /dev/null +++ b/exercise.main/Interfaces/IFillable.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 IFillable + { + public List Products { get; } + + public void AddFillings(IInventoryProduct filling) + { + + } + } +} diff --git a/exercise.main/Interfaces/IInventoryProduct.cs b/exercise.main/Interfaces/IInventoryProduct.cs new file mode 100644 index 00000000..a67ac89f --- /dev/null +++ b/exercise.main/Interfaces/IInventoryProduct.cs @@ -0,0 +1,17 @@ +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 SKU { get; } + public float Price { get; } + + public float GetFinalPrice(); + + } +} diff --git a/exercise.main/InventoryProduct.cs b/exercise.main/InventoryProduct.cs new file mode 100644 index 00000000..690fa8da --- /dev/null +++ b/exercise.main/InventoryProduct.cs @@ -0,0 +1,32 @@ +using exercise.main.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace exercise.main +{ + public class InventoryProduct : IInventoryProduct + { + private List _products; + + public string SKU { get; } + public float Price { get; } + public float DiscountedPrice { get; } + public bool IsFilling { get; } + public List InventoryProducts { get { return _products; } } + + public InventoryProduct(string sku, float price, bool isFilling) + { + SKU = sku; + Price = price; + IsFilling = isFilling; + } + + public void SetDiscountPrice(float discountPrice) + { + + } + } +} diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs new file mode 100644 index 00000000..1c8c453f --- /dev/null +++ b/exercise.main/Items/Bagel.cs @@ -0,0 +1,46 @@ +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 _sku; + private float _price; + private float _discountedPrice; + private List _fillings; + + public string SKU => throw new NotImplementedException(); + + public float Price => throw new NotImplementedException(); + + public float DiscountedPrice => throw new NotImplementedException(); + + public List Products => throw new NotImplementedException(); + + public void SetDiscountPrice(float discountPrice) + { + throw new NotImplementedException(); + } + + public float GetFinalPrice() + { + throw new NotImplementedException(); + } + + public void AddFillings(IInventoryProduct filling) + { + throw new NotImplementedException(); + } + + public Bagel(string sku, float price) { + _sku = sku; + _price = price; + } + } +} diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs new file mode 100644 index 00000000..c5f108dd --- /dev/null +++ b/exercise.main/Items/Coffee.cs @@ -0,0 +1,37 @@ +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 _sku; + private float _price; + + public string SKU => throw new NotImplementedException(); + + public float Price => throw new NotImplementedException(); + + public float DiscountedPrice => throw new NotImplementedException(); + + public float GetFinalPrice() + { + throw new NotImplementedException(); + } + + public void SetDiscountPrice(float discountPrice) + { + throw new NotImplementedException(); + } + + public Coffee(string sku, float price) + { + _sku = sku; + _price = price; + } + } +} diff --git a/exercise.main/Items/Filling.cs b/exercise.main/Items/Filling.cs new file mode 100644 index 00000000..8a976478 --- /dev/null +++ b/exercise.main/Items/Filling.cs @@ -0,0 +1,30 @@ +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 _sku; + private float _price; + + public string SKU => throw new NotImplementedException(); + + public float Price => throw new NotImplementedException(); + + public float GetFinalPrice() + { + throw new NotImplementedException(); + } + + public Filling(string sku, float price) + { + _sku = sku; + _price = price; + } + } +} diff --git a/exercise.main/Products.cs b/exercise.main/Products.cs new file mode 100644 index 00000000..c8a2e343 --- /dev/null +++ b/exercise.main/Products.cs @@ -0,0 +1,102 @@ +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 +{ + 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 IInventoryProduct BGLO() + { + return new Bagel("BGLO", 0.49f); + } + + public static IInventoryProduct BGLP() + { + return new Bagel("BGLP", 0.39f); + } + + public static IInventoryProduct BGLE() + { + return new Bagel("BGLE", 0.49f); + } + + public static IInventoryProduct BGLS() + { + return new Bagel("BGLS", 0.49f); + } + + public static IInventoryProduct COFB() + { + return new Coffee("COFB", 0.99f); + } + + public static IInventoryProduct COFW() + { + return new Coffee("COFB", 1.19f); + } + + public static IInventoryProduct COFC() + { + return new Coffee("COFC", 1.29f); + } + + public static IInventoryProduct COFL() + { + return new Coffee("COFL", 1.29f); + } + + public static IInventoryProduct FILB() + { + return new Filling("FILB", 0.12f); + } + + public static IInventoryProduct FILE() + { + return new Filling("FILE", 0.12f); + } + + public static IInventoryProduct FILC() + { + return new Coffee("FILC", 0.12f); + } + + public static IInventoryProduct FILX() + { + return new Coffee("FILX", 0.12f); + } + + public static IInventoryProduct FILS() + { + return new Coffee("FILS", 0.12f); + } + + public static IInventoryProduct FILH() + { + return new Coffee("FILH", 0.12f); + } + } +} diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 7bdb8968..3aa7ddfb 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -1,15 +1,101 @@ +using exercise.main; +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 Test1() + public void RemoveItemFromBasketTask2() { - Assert.Pass(); + 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)); } } \ No newline at end of file From c315c24465fe12edee6b856133e510dd1407b97d Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 11:35:11 +0200 Subject: [PATCH 02/11] feat: basket functionality --- exercise.main/Basket.cs | 16 ++++++++++++++ exercise.main/Interfaces/IFillable.cs | 5 +++-- exercise.main/InventoryProduct.cs | 32 --------------------------- exercise.main/Items/Bagel.cs | 19 ++++++++-------- exercise.main/Items/Coffee.cs | 11 ++++----- exercise.main/Items/Filling.cs | 6 ++--- exercise.tests/UnitTest1.cs | 2 +- exercise.tests/exercise.tests.csproj | 4 ++++ 8 files changed, 43 insertions(+), 52 deletions(-) delete mode 100644 exercise.main/InventoryProduct.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index aba211d1..a4826fa5 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using exercise.main.Exceptions; namespace exercise.main { @@ -26,17 +27,32 @@ public void Add(IInventoryProduct product) if (product == null) throw new ArgumentNullException(); + if (_products.Count == _capacity) + throw new BasketFullException(); + _products.Add(product); } 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); } public void ChangeCapacity(int newCapacity) { _capacity = newCapacity; } + + public float GetTotalCost() + { + return _products.Sum(p => p.GetFinalPrice()); + } } } diff --git a/exercise.main/Interfaces/IFillable.cs b/exercise.main/Interfaces/IFillable.cs index 052ee001..7885e145 100644 --- a/exercise.main/Interfaces/IFillable.cs +++ b/exercise.main/Interfaces/IFillable.cs @@ -3,14 +3,15 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using exercise.main.Items; namespace exercise.main.Interfaces { public interface IFillable { - public List Products { get; } + public List Fillings { get; } - public void AddFillings(IInventoryProduct filling) + public void AddFillings(Filling filling) { } diff --git a/exercise.main/InventoryProduct.cs b/exercise.main/InventoryProduct.cs deleted file mode 100644 index 690fa8da..00000000 --- a/exercise.main/InventoryProduct.cs +++ /dev/null @@ -1,32 +0,0 @@ -using exercise.main.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace exercise.main -{ - public class InventoryProduct : IInventoryProduct - { - private List _products; - - public string SKU { get; } - public float Price { get; } - public float DiscountedPrice { get; } - public bool IsFilling { get; } - public List InventoryProducts { get { return _products; } } - - public InventoryProduct(string sku, float price, bool isFilling) - { - SKU = sku; - Price = price; - IsFilling = isFilling; - } - - public void SetDiscountPrice(float discountPrice) - { - - } - } -} diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs index 1c8c453f..d29c81bf 100644 --- a/exercise.main/Items/Bagel.cs +++ b/exercise.main/Items/Bagel.cs @@ -13,29 +13,30 @@ public class Bagel : IInventoryProduct, IFillable, IDiscountable private string _sku; private float _price; private float _discountedPrice; - private List _fillings; + private List _fillings = new(); - public string SKU => throw new NotImplementedException(); + public string SKU { get { return _sku; } } - public float Price => throw new NotImplementedException(); + public float Price { get { return _price; } } - public float DiscountedPrice => throw new NotImplementedException(); + public float DiscountedPrice { get { return _discountedPrice; } } - public List Products => throw new NotImplementedException(); + List IFillable.Fillings { get { return _fillings; } } public void SetDiscountPrice(float discountPrice) { - throw new NotImplementedException(); + _discountedPrice = discountPrice; } public float GetFinalPrice() { - throw new NotImplementedException(); + float sumFillings = _fillings.Sum(f => f.GetFinalPrice()); + return sumFillings + _price; } - public void AddFillings(IInventoryProduct filling) + public void AddFillings(Filling filling) { - throw new NotImplementedException(); + _fillings.Add(filling); } public Bagel(string sku, float price) { diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs index c5f108dd..db78c173 100644 --- a/exercise.main/Items/Coffee.cs +++ b/exercise.main/Items/Coffee.cs @@ -11,21 +11,22 @@ public class Coffee : IInventoryProduct, IDiscountable { private string _sku; private float _price; + private float _discountedPrice; - public string SKU => throw new NotImplementedException(); + public string SKU { get { return _sku; } } - public float Price => throw new NotImplementedException(); + public float Price { get { return _price; } } - public float DiscountedPrice => throw new NotImplementedException(); + public float DiscountedPrice { get { return _discountedPrice; } } public float GetFinalPrice() { - throw new NotImplementedException(); + return _price; } public void SetDiscountPrice(float discountPrice) { - throw new NotImplementedException(); + _discountedPrice = discountPrice; } public Coffee(string sku, float price) diff --git a/exercise.main/Items/Filling.cs b/exercise.main/Items/Filling.cs index 8a976478..f95240ba 100644 --- a/exercise.main/Items/Filling.cs +++ b/exercise.main/Items/Filling.cs @@ -12,13 +12,13 @@ public class Filling : IInventoryProduct private string _sku; private float _price; - public string SKU => throw new NotImplementedException(); + public string SKU { get { return _sku; } } - public float Price => throw new NotImplementedException(); + public float Price { get { return _price; } } public float GetFinalPrice() { - throw new NotImplementedException(); + return _price; } public Filling(string sku, float price) diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 3aa7ddfb..bae4c48c 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -67,7 +67,7 @@ public void RemoveItemNotInBasketFromBasketTask() var basketProducts = basket.Products.ToList(); - Assert.Throws(() => basket.Remove("BGLO");); + Assert.Throws(() => basket.Remove("BGLO")); } [Test] 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 ad1f0c86e704dc89d47f5b3c37c7eccf09339067 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 11:40:25 +0200 Subject: [PATCH 03/11] fix: price stored in Decimal --- exercise.main/Basket.cs | 2 +- exercise.main/Interfaces/IDiscountable.cs | 4 +-- exercise.main/Interfaces/IInventoryProduct.cs | 4 +-- exercise.main/Items/Bagel.cs | 16 +++++------ exercise.main/Items/Coffee.cs | 14 +++++----- exercise.main/Items/Filling.cs | 8 +++--- exercise.main/Products.cs | 28 +++++++++---------- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index a4826fa5..149c92c1 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -50,7 +50,7 @@ public void ChangeCapacity(int newCapacity) _capacity = newCapacity; } - public float GetTotalCost() + public Decimal GetTotalCost() { return _products.Sum(p => p.GetFinalPrice()); } diff --git a/exercise.main/Interfaces/IDiscountable.cs b/exercise.main/Interfaces/IDiscountable.cs index 0ebbba09..8fec4506 100644 --- a/exercise.main/Interfaces/IDiscountable.cs +++ b/exercise.main/Interfaces/IDiscountable.cs @@ -8,8 +8,8 @@ namespace exercise.main.Interfaces { public interface IDiscountable { - public float DiscountedPrice { get; } + public Decimal DiscountedPrice { get; } - public void SetDiscountPrice(float discountPrice); + public void SetDiscountPrice(Decimal discountPrice); } } diff --git a/exercise.main/Interfaces/IInventoryProduct.cs b/exercise.main/Interfaces/IInventoryProduct.cs index a67ac89f..9d649d2b 100644 --- a/exercise.main/Interfaces/IInventoryProduct.cs +++ b/exercise.main/Interfaces/IInventoryProduct.cs @@ -9,9 +9,9 @@ namespace exercise.main.Interfaces public interface IInventoryProduct { public string SKU { get; } - public float Price { get; } + public Decimal Price { get; } - public float GetFinalPrice(); + public Decimal GetFinalPrice(); } } diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs index d29c81bf..1c9d91bf 100644 --- a/exercise.main/Items/Bagel.cs +++ b/exercise.main/Items/Bagel.cs @@ -11,26 +11,26 @@ namespace exercise.main.Items public class Bagel : IInventoryProduct, IFillable, IDiscountable { private string _sku; - private float _price; - private float _discountedPrice; + private Decimal _price; + private Decimal _discountedPrice; private List _fillings = new(); public string SKU { get { return _sku; } } - public float Price { get { return _price; } } + public Decimal Price { get { return _price; } } - public float DiscountedPrice { get { return _discountedPrice; } } + public Decimal DiscountedPrice { get { return _discountedPrice; } } List IFillable.Fillings { get { return _fillings; } } - public void SetDiscountPrice(float discountPrice) + public void SetDiscountPrice(Decimal discountPrice) { _discountedPrice = discountPrice; } - public float GetFinalPrice() + public Decimal GetFinalPrice() { - float sumFillings = _fillings.Sum(f => f.GetFinalPrice()); + var sumFillings = _fillings.Sum(f => f.GetFinalPrice()); return sumFillings + _price; } @@ -39,7 +39,7 @@ public void AddFillings(Filling filling) _fillings.Add(filling); } - public Bagel(string sku, float price) { + public Bagel(string sku, Decimal price) { _sku = sku; _price = price; } diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs index db78c173..3e68fbcd 100644 --- a/exercise.main/Items/Coffee.cs +++ b/exercise.main/Items/Coffee.cs @@ -10,26 +10,26 @@ namespace exercise.main.Items public class Coffee : IInventoryProduct, IDiscountable { private string _sku; - private float _price; - private float _discountedPrice; + private Decimal _price; + private Decimal _discountedPrice; public string SKU { get { return _sku; } } - public float Price { get { return _price; } } + public Decimal Price { get { return _price; } } - public float DiscountedPrice { get { return _discountedPrice; } } + public Decimal DiscountedPrice { get { return _discountedPrice; } } - public float GetFinalPrice() + public Decimal GetFinalPrice() { return _price; } - public void SetDiscountPrice(float discountPrice) + public void SetDiscountPrice(Decimal discountPrice) { _discountedPrice = discountPrice; } - public Coffee(string sku, float price) + public Coffee(string sku, Decimal price) { _sku = sku; _price = price; diff --git a/exercise.main/Items/Filling.cs b/exercise.main/Items/Filling.cs index f95240ba..7f6f03d3 100644 --- a/exercise.main/Items/Filling.cs +++ b/exercise.main/Items/Filling.cs @@ -10,18 +10,18 @@ namespace exercise.main.Items public class Filling : IInventoryProduct { private string _sku; - private float _price; + private Decimal _price; public string SKU { get { return _sku; } } - public float Price { get { return _price; } } + public Decimal Price { get { return _price; } } - public float GetFinalPrice() + public Decimal GetFinalPrice() { return _price; } - public Filling(string sku, float price) + public Filling(string sku, Decimal price) { _sku = sku; _price = price; diff --git a/exercise.main/Products.cs b/exercise.main/Products.cs index c8a2e343..5b0caec2 100644 --- a/exercise.main/Products.cs +++ b/exercise.main/Products.cs @@ -31,72 +31,72 @@ FILH 0.12 Filling Ham public static IInventoryProduct BGLO() { - return new Bagel("BGLO", 0.49f); + return new Bagel("BGLO", 0.49m); } public static IInventoryProduct BGLP() { - return new Bagel("BGLP", 0.39f); + return new Bagel("BGLP", 0.39m); } public static IInventoryProduct BGLE() { - return new Bagel("BGLE", 0.49f); + return new Bagel("BGLE", 0.49m); } public static IInventoryProduct BGLS() { - return new Bagel("BGLS", 0.49f); + return new Bagel("BGLS", 0.49m); } public static IInventoryProduct COFB() { - return new Coffee("COFB", 0.99f); + return new Coffee("COFB", 0.99m); } public static IInventoryProduct COFW() { - return new Coffee("COFB", 1.19f); + return new Coffee("COFB", 1.19m); } public static IInventoryProduct COFC() { - return new Coffee("COFC", 1.29f); + return new Coffee("COFC", 1.29m); } public static IInventoryProduct COFL() { - return new Coffee("COFL", 1.29f); + return new Coffee("COFL", 1.29m); } public static IInventoryProduct FILB() { - return new Filling("FILB", 0.12f); + return new Filling("FILB", 0.12m); } public static IInventoryProduct FILE() { - return new Filling("FILE", 0.12f); + return new Filling("FILE", 0.12m); } public static IInventoryProduct FILC() { - return new Coffee("FILC", 0.12f); + return new Coffee("FILC", 0.12m); } public static IInventoryProduct FILX() { - return new Coffee("FILX", 0.12f); + return new Coffee("FILX", 0.12m); } public static IInventoryProduct FILS() { - return new Coffee("FILS", 0.12f); + return new Coffee("FILS", 0.12m); } public static IInventoryProduct FILH() { - return new Coffee("FILH", 0.12f); + return new Coffee("FILH", 0.12m); } } } From 6b277989f573315b4064142fcec900fb7ab6830d Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 11:53:13 +0200 Subject: [PATCH 04/11] feat: core implemented --- exercise.main/Products.cs | 36 ++++++++++++++++----------------- exercise.tests/UnitTest1.cs | 40 +++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 18 deletions(-) diff --git a/exercise.main/Products.cs b/exercise.main/Products.cs index 5b0caec2..72f70e40 100644 --- a/exercise.main/Products.cs +++ b/exercise.main/Products.cs @@ -29,74 +29,74 @@ FILS 0.12 Filling Smoked Salmon FILH 0.12 Filling Ham */ - public static IInventoryProduct BGLO() + public static Bagel BGLO() { return new Bagel("BGLO", 0.49m); } - public static IInventoryProduct BGLP() + public static Bagel BGLP() { return new Bagel("BGLP", 0.39m); } - public static IInventoryProduct BGLE() + public static Bagel BGLE() { return new Bagel("BGLE", 0.49m); } - public static IInventoryProduct BGLS() + public static Bagel BGLS() { return new Bagel("BGLS", 0.49m); } - public static IInventoryProduct COFB() + public static Coffee COFB() { return new Coffee("COFB", 0.99m); } - public static IInventoryProduct COFW() + public static Coffee COFW() { return new Coffee("COFB", 1.19m); } - public static IInventoryProduct COFC() + public static Coffee COFC() { return new Coffee("COFC", 1.29m); } - public static IInventoryProduct COFL() + public static Coffee COFL() { return new Coffee("COFL", 1.29m); } - public static IInventoryProduct FILB() + public static Filling FILB() { return new Filling("FILB", 0.12m); } - public static IInventoryProduct FILE() + public static Filling FILE() { return new Filling("FILE", 0.12m); } - public static IInventoryProduct FILC() + public static Filling FILC() { - return new Coffee("FILC", 0.12m); + return new Filling("FILC", 0.12m); } - public static IInventoryProduct FILX() + public static Filling FILX() { - return new Coffee("FILX", 0.12m); + return new Filling("FILX", 0.12m); } - public static IInventoryProduct FILS() + public static Filling FILS() { - return new Coffee("FILS", 0.12m); + return new Filling("FILS", 0.12m); } - public static IInventoryProduct FILH() + public static Filling FILH() { - return new Coffee("FILH", 0.12m); + return new Filling("FILH", 0.12m); } } } diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index bae4c48c..0d91bbf5 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -98,4 +98,44 @@ public void ChangeBasketCapacityTask() 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 AddFillingToBagel() + { + 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)); + } } \ No newline at end of file From 44a3fc87f98fa76b5a6278499bb556d60d2436b6 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 14:36:25 +0200 Subject: [PATCH 05/11] feat: discounts --- exercise.main/Basket.cs | 68 +++++++++++++++++++- exercise.main/Discount.cs | 57 +++++++++++++++++ exercise.main/Discounts.cs | 77 +++++++++++++++++++++++ exercise.main/Interfaces/IDiscountable.cs | 3 +- exercise.main/Items/Bagel.cs | 6 ++ exercise.main/Items/Coffee.cs | 2 + exercise.main/Prices.cs | 29 +++++++++ exercise.main/Products.cs | 42 ++++++++----- exercise.tests/UnitTest1.cs | 19 +++++- 9 files changed, 284 insertions(+), 19 deletions(-) create mode 100644 exercise.main/Discount.cs create mode 100644 exercise.main/Discounts.cs create mode 100644 exercise.main/Prices.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index 149c92c1..065ec0c2 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -11,10 +11,10 @@ namespace exercise.main public class Basket { private int _capacity; - private List _products = new(); + private List _products = new(); - public List Products { get { return _products; } } + public List Products { get { return _products; } } public int Capacity { get { return _capacity; } } public Basket(int capacity) @@ -22,7 +22,7 @@ public Basket(int capacity) _capacity = capacity; } - public void Add(IInventoryProduct product) + public void Add(IDiscountable product) { if (product == null) throw new ArgumentNullException(); @@ -31,6 +31,8 @@ public void Add(IInventoryProduct product) throw new BasketFullException(); _products.Add(product); + + CalculateDiscounts(); } public void Remove(string SKU) @@ -43,6 +45,8 @@ public void Remove(string SKU) var itemToRemove = _products.Where(p => p.SKU == SKU).FirstOrDefault(); _products.Remove(itemToRemove); + + CalculateDiscounts(); } public void ChangeCapacity(int newCapacity) @@ -54,5 +58,63 @@ 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(); + Console.WriteLine("DISCOUNT POPPED"); + missingRequirements = true; + break; + } + } + + if (missingRequirements) + continue; + + // apply discounted prices if requirement passed + // move discounted items to new list + + Console.WriteLine("DISCOUNT PASSED"); + + 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).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); + } } } diff --git a/exercise.main/Discount.cs b/exercise.main/Discount.cs new file mode 100644 index 00000000..6227a1d1 --- /dev/null +++ b/exercise.main/Discount.cs @@ -0,0 +1,57 @@ +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 NewTotalPrice; + + public Discount(Dictionary itemRequirementAmountDict, Dictionary itemDiscountedPriceDict) + { + ItemRequirementAmountDict = itemRequirementAmountDict; + ItemDiscountedPriceDict = itemDiscountedPriceDict; + + SavedAmount = CalculateSavedAmount(); + 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 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/Discounts.cs b/exercise.main/Discounts.cs new file mode 100644 index 00000000..4928df6a --- /dev/null +++ b/exercise.main/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 +{ + 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/Interfaces/IDiscountable.cs b/exercise.main/Interfaces/IDiscountable.cs index 8fec4506..2fa4d233 100644 --- a/exercise.main/Interfaces/IDiscountable.cs +++ b/exercise.main/Interfaces/IDiscountable.cs @@ -6,9 +6,10 @@ namespace exercise.main.Interfaces { - public interface IDiscountable + public interface IDiscountable : IInventoryProduct { public Decimal DiscountedPrice { get; } + public bool IsDiscounted { get; set; } public void SetDiscountPrice(Decimal discountPrice); } diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs index 1c9d91bf..eb79df56 100644 --- a/exercise.main/Items/Bagel.cs +++ b/exercise.main/Items/Bagel.cs @@ -23,6 +23,8 @@ public class Bagel : IInventoryProduct, IFillable, IDiscountable List IFillable.Fillings { get { return _fillings; } } + public bool IsDiscounted { get; set; } + public void SetDiscountPrice(Decimal discountPrice) { _discountedPrice = discountPrice; @@ -31,6 +33,9 @@ public void SetDiscountPrice(Decimal discountPrice) public Decimal GetFinalPrice() { var sumFillings = _fillings.Sum(f => f.GetFinalPrice()); + if (IsDiscounted) + return sumFillings + DiscountedPrice; + return sumFillings + _price; } @@ -42,6 +47,7 @@ public void AddFillings(Filling filling) public Bagel(string sku, Decimal price) { _sku = sku; _price = price; + IsDiscounted = false; } } } diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs index 3e68fbcd..8ec9d218 100644 --- a/exercise.main/Items/Coffee.cs +++ b/exercise.main/Items/Coffee.cs @@ -19,6 +19,8 @@ public class Coffee : IInventoryProduct, IDiscountable public Decimal DiscountedPrice { get { return _discountedPrice; } } + public bool IsDiscounted { get; set; } + public Decimal GetFinalPrice() { return _price; diff --git a/exercise.main/Prices.cs b/exercise.main/Prices.cs new file mode 100644 index 00000000..4e8d971e --- /dev/null +++ b/exercise.main/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 +{ + 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/Products.cs b/exercise.main/Products.cs index 72f70e40..aead9c47 100644 --- a/exercise.main/Products.cs +++ b/exercise.main/Products.cs @@ -31,72 +31,86 @@ FILH 0.12 Filling Ham public static Bagel BGLO() { - return new Bagel("BGLO", 0.49m); + var sku = "BGLO"; + return new Bagel(sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLP() { - return new Bagel("BGLP", 0.39m); + var sku = "BGLP"; + return new Bagel(sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLE() { - return new Bagel("BGLE", 0.49m); + var sku = "BGLE"; + return new Bagel(sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLS() { - return new Bagel("BGLS", 0.49m); + var sku = "BGLS"; + return new Bagel(sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFB() { - return new Coffee("COFB", 0.99m); + var sku = "COFB"; + return new Coffee(sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFW() { - return new Coffee("COFB", 1.19m); + var sku = "COFW"; + return new Coffee(sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFC() { - return new Coffee("COFC", 1.29m); + var sku = "COFC"; + return new Coffee(sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFL() { - return new Coffee("COFL", 1.29m); + var sku = "COFL"; + return new Coffee(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILB() { - return new Filling("FILB", 0.12m); + var sku = "FILB"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILE() { - return new Filling("FILE", 0.12m); + var sku = "FILE"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILC() { - return new Filling("FILC", 0.12m); + var sku = "FILC"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILX() { - return new Filling("FILX", 0.12m); + var sku = "FILX"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILS() { - return new Filling("FILS", 0.12m); + var sku = "FILS"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } public static Filling FILH() { - return new Filling("FILH", 0.12m); + var sku = "FILH"; + return new Filling(sku, Prices.SkuToPriceMap[sku]); } } } diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 0d91bbf5..a4451a8c 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -124,7 +124,7 @@ public void CheckBasketPriceTest2() [Test] - public void AddFillingToBagel() + public void AddFillingToBagelTest() { var basket = new Basket(6); @@ -138,4 +138,21 @@ public void AddFillingToBagel() Assert.That(price, Is.EqualTo(Products.BGLO().Price + Products.FILS().Price)); } + + [Test] + public void AddBagelDiscount() + { + 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)); + } } \ No newline at end of file From 1065dde74ba55c7b00de79b095b0c3e3ba3a799a Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 14:57:36 +0200 Subject: [PATCH 06/11] fix: discounts sorted by saved amount --- exercise.main/Basket.cs | 5 +-- exercise.main/Discount.cs | 20 +++++++++++ exercise.tests/UnitTest1.cs | 69 ++++++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index 065ec0c2..ba7728b6 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -79,7 +79,6 @@ private void CalculateDiscounts() if (count < kvp.Value) { discounts.Pop(); - Console.WriteLine("DISCOUNT POPPED"); missingRequirements = true; break; } @@ -91,8 +90,6 @@ private void CalculateDiscounts() // apply discounted prices if requirement passed // move discounted items to new list - Console.WriteLine("DISCOUNT PASSED"); - foreach (var kvp in discount.ItemRequirementAmountDict) { var newPrice = discount.ItemDiscountedPriceDict.GetValueOrDefault(kvp.Key); @@ -101,7 +98,7 @@ private void CalculateDiscounts() discounted.ForEach(p => p.IsDiscounted = true); discountedItems.AddRange(discounted); - var itemsToRemove = undiscountedItems.Where(p => p.SKU == kvp.Key).ToList(); + var itemsToRemove = undiscountedItems.Where(p => p.SKU == kvp.Key).Take(kvp.Value).ToList(); itemsToRemove.ForEach(i => undiscountedItems.Remove(i)); } } diff --git a/exercise.main/Discount.cs b/exercise.main/Discount.cs index 6227a1d1..ed368e4a 100644 --- a/exercise.main/Discount.cs +++ b/exercise.main/Discount.cs @@ -11,6 +11,7 @@ public class Discount public Dictionary ItemRequirementAmountDict; public Dictionary ItemDiscountedPriceDict; public Decimal SavedAmount; + public Decimal SavingsPercentage; public Decimal NewTotalPrice; public Discount(Dictionary itemRequirementAmountDict, Dictionary itemDiscountedPriceDict) @@ -19,6 +20,7 @@ public Discount(Dictionary itemRequirementAmountDict, Dictionary Date: Mon, 11 Aug 2025 15:49:13 +0200 Subject: [PATCH 07/11] feat: printers interface --- exercise.main/Basket.cs | 13 ++++++++ exercise.main/{ => Collections}/Discounts.cs | 2 +- exercise.main/{ => Collections}/Prices.cs | 4 +-- exercise.main/{ => Collections}/Products.cs | 30 +++++++++---------- exercise.main/Discount.cs | 3 +- exercise.main/Interfaces/IInventoryProduct.cs | 1 + exercise.main/Interfaces/IReceiptPrinter.cs | 13 ++++++++ exercise.main/Items/Bagel.cs | 6 +++- exercise.main/Items/Coffee.cs | 6 +++- exercise.main/Items/Filling.cs | 6 +++- .../ReceiptPrinters/ConsoleReceiptPrinter.cs | 17 +++++++++++ exercise.tests/UnitTest1.cs | 20 +++++++++++++ 12 files changed, 99 insertions(+), 22 deletions(-) rename exercise.main/{ => Collections}/Discounts.cs (98%) rename exercise.main/{ => Collections}/Prices.cs (86%) rename exercise.main/{ => Collections}/Products.cs (62%) create mode 100644 exercise.main/Interfaces/IReceiptPrinter.cs create mode 100644 exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index ba7728b6..75c0c568 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading.Tasks; using exercise.main.Exceptions; +using exercise.main.Collections; namespace exercise.main { @@ -113,5 +114,17 @@ private void ResetDiscountedPrices() { _products.ForEach(p => p.IsDiscounted = false); } + + public void PrintReceipt(IReceiptPrinter printer) + { + var groupedByItem = _products.GroupBy(p => p.SKU); + + string itemsString = ""; + + foreach (var group in groupedByItem) + { + + } + } } } diff --git a/exercise.main/Discounts.cs b/exercise.main/Collections/Discounts.cs similarity index 98% rename from exercise.main/Discounts.cs rename to exercise.main/Collections/Discounts.cs index 4928df6a..716cf91f 100644 --- a/exercise.main/Discounts.cs +++ b/exercise.main/Collections/Discounts.cs @@ -4,7 +4,7 @@ using System.Text; using System.Threading.Tasks; -namespace exercise.main +namespace exercise.main.Collections { public static class Discounts { diff --git a/exercise.main/Prices.cs b/exercise.main/Collections/Prices.cs similarity index 86% rename from exercise.main/Prices.cs rename to exercise.main/Collections/Prices.cs index 4e8d971e..007e59b4 100644 --- a/exercise.main/Prices.cs +++ b/exercise.main/Collections/Prices.cs @@ -4,11 +4,11 @@ using System.Text; using System.Threading.Tasks; -namespace exercise.main +namespace exercise.main.Collections { public static class Prices { - public static Dictionary SkuToPriceMap = new Dictionary + public static Dictionary SkuToPriceMap = new Dictionary { {"BGLO", 0.49m}, {"BGLP", 0.39m}, diff --git a/exercise.main/Products.cs b/exercise.main/Collections/Products.cs similarity index 62% rename from exercise.main/Products.cs rename to exercise.main/Collections/Products.cs index aead9c47..1658f468 100644 --- a/exercise.main/Products.cs +++ b/exercise.main/Collections/Products.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using exercise.main.Items; -namespace exercise.main +namespace exercise.main.Collections { public static class Products { @@ -32,85 +32,85 @@ FILH 0.12 Filling Ham public static Bagel BGLO() { var sku = "BGLO"; - return new Bagel(sku, Prices.SkuToPriceMap[sku]); + return new Bagel("Onion Bagel" ,sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLP() { var sku = "BGLP"; - return new Bagel(sku, Prices.SkuToPriceMap[sku]); + return new Bagel("Plain Bagel", sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLE() { var sku = "BGLE"; - return new Bagel(sku, Prices.SkuToPriceMap[sku]); + return new Bagel("Everything Bagel", sku, Prices.SkuToPriceMap[sku]); } public static Bagel BGLS() { var sku = "BGLS"; - return new Bagel(sku, Prices.SkuToPriceMap[sku]); + return new Bagel("Sesame Bagel", sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFB() { var sku = "COFB"; - return new Coffee(sku, Prices.SkuToPriceMap[sku]); + return new Coffee("Black Coffee", sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFW() { var sku = "COFW"; - return new Coffee(sku, Prices.SkuToPriceMap[sku]); + return new Coffee("White Coffee", sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFC() { var sku = "COFC"; - return new Coffee(sku, Prices.SkuToPriceMap[sku]); + return new Coffee("Capuccino", sku, Prices.SkuToPriceMap[sku]); } public static Coffee COFL() { var sku = "COFL"; - return new Coffee(sku, Prices.SkuToPriceMap[sku]); + return new Coffee("Latte", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILB() { var sku = "FILB"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Bacon", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILE() { var sku = "FILE"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Egg", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILC() { var sku = "FILC"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Cheese", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILX() { var sku = "FILX"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Cream Cheese", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILS() { var sku = "FILS"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Smoked Salmon", sku, Prices.SkuToPriceMap[sku]); } public static Filling FILH() { var sku = "FILH"; - return new Filling(sku, Prices.SkuToPriceMap[sku]); + return new Filling("Ham", sku, Prices.SkuToPriceMap[sku]); } } } diff --git a/exercise.main/Discount.cs b/exercise.main/Discount.cs index ed368e4a..86aff8a3 100644 --- a/exercise.main/Discount.cs +++ b/exercise.main/Discount.cs @@ -1,4 +1,5 @@ -using System; +using exercise.main.Collections; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/exercise.main/Interfaces/IInventoryProduct.cs b/exercise.main/Interfaces/IInventoryProduct.cs index 9d649d2b..6604cf35 100644 --- a/exercise.main/Interfaces/IInventoryProduct.cs +++ b/exercise.main/Interfaces/IInventoryProduct.cs @@ -8,6 +8,7 @@ namespace exercise.main.Interfaces { public interface IInventoryProduct { + public string Name { get; } public string SKU { get; } public Decimal Price { get; } 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 index eb79df56..919d865c 100644 --- a/exercise.main/Items/Bagel.cs +++ b/exercise.main/Items/Bagel.cs @@ -10,6 +10,7 @@ namespace exercise.main.Items { public class Bagel : IInventoryProduct, IFillable, IDiscountable { + private string _name; private string _sku; private Decimal _price; private Decimal _discountedPrice; @@ -25,6 +26,8 @@ public class Bagel : IInventoryProduct, IFillable, IDiscountable public bool IsDiscounted { get; set; } + public string Name { get { return _name; } } + public void SetDiscountPrice(Decimal discountPrice) { _discountedPrice = discountPrice; @@ -44,7 +47,8 @@ public void AddFillings(Filling filling) _fillings.Add(filling); } - public Bagel(string sku, Decimal price) { + 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 index 8ec9d218..f54976f7 100644 --- a/exercise.main/Items/Coffee.cs +++ b/exercise.main/Items/Coffee.cs @@ -9,6 +9,7 @@ namespace exercise.main.Items { public class Coffee : IInventoryProduct, IDiscountable { + private string _name; private string _sku; private Decimal _price; private Decimal _discountedPrice; @@ -21,6 +22,8 @@ public class Coffee : IInventoryProduct, IDiscountable public bool IsDiscounted { get; set; } + public string Name { get { return _name; } } + public Decimal GetFinalPrice() { return _price; @@ -31,8 +34,9 @@ public void SetDiscountPrice(Decimal discountPrice) _discountedPrice = discountPrice; } - public Coffee(string sku, Decimal price) + 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 index 7f6f03d3..cc8cd7b9 100644 --- a/exercise.main/Items/Filling.cs +++ b/exercise.main/Items/Filling.cs @@ -9,6 +9,7 @@ namespace exercise.main.Items { public class Filling : IInventoryProduct { + private string _name; private string _sku; private Decimal _price; @@ -16,13 +17,16 @@ public class Filling : IInventoryProduct public Decimal Price { get { return _price; } } + public string Name => throw new NotImplementedException(); + public Decimal GetFinalPrice() { return _price; } - public Filling(string sku, Decimal price) + public Filling(string name, string sku, Decimal price) { + _name = name; _sku = sku; _price = price; } diff --git a/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs b/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs new file mode 100644 index 00000000..32d3c9d7 --- /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) + { + throw new NotImplementedException(); + } + } +} diff --git a/exercise.tests/UnitTest1.cs b/exercise.tests/UnitTest1.cs index 3d3f9b0b..543d1271 100644 --- a/exercise.tests/UnitTest1.cs +++ b/exercise.tests/UnitTest1.cs @@ -1,4 +1,5 @@ using exercise.main; +using exercise.main.Collections; using exercise.main.Exceptions; using exercise.main.Items; @@ -156,6 +157,25 @@ public void AddBGLODiscountTest() 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() { From 0e6b9391f42d55bd169e7ccc21943e37978d423d Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Mon, 11 Aug 2025 15:50:06 +0200 Subject: [PATCH 08/11] feat: basket print --- exercise.main/Basket.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index 75c0c568..05d88666 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -119,11 +119,12 @@ public void PrintReceipt(IReceiptPrinter printer) { var groupedByItem = _products.GroupBy(p => p.SKU); - string itemsString = ""; + string CombinedItemsString = ""; foreach (var group in groupedByItem) - { - + { + var name = group.FirstOrDefault().Name; + var itemString = $"{name}"; } } } From a59137a80d37f27fc3b9e0b9b2b9950e918509e7 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Tue, 12 Aug 2025 08:41:51 +0200 Subject: [PATCH 09/11] feat: console printer --- exercise.main/Basket.cs | 51 +++++++++++++++++-- exercise.main/Program.cs | 26 +++++++++- .../ReceiptPrinters/ConsoleReceiptPrinter.cs | 2 +- 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index 05d88666..524daf18 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -1,11 +1,12 @@ -using exercise.main.Interfaces; +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; -using exercise.main.Exceptions; -using exercise.main.Collections; namespace exercise.main { @@ -91,6 +92,8 @@ private void CalculateDiscounts() // 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); @@ -119,13 +122,51 @@ public void PrintReceipt(IReceiptPrinter printer) { var groupedByItem = _products.GroupBy(p => p.SKU); - string CombinedItemsString = ""; + string combinedItemsString = ""; foreach (var group in groupedByItem) { var name = group.FirstOrDefault().Name; - var itemString = $"{name}"; + var price = group.Sum(p => p.GetFinalPrice()); + var itemString = $"{name}" + " " + $"{group.Count()}" + " " + $"{price.ToString("F2")}" + "\n"; + + combinedItemsString += itemString; } + + 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"; + + + return startString; + } + + private string GetReceiptEndString() + { + var totalPrice = GetTotalCost(); + + var endString = ""; + 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/Program.cs b/exercise.main/Program.cs index 3751555c..edf43f98 100644 --- a/exercise.main/Program.cs +++ b/exercise.main/Program.cs @@ -1,2 +1,26 @@ // 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(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(); + +var printer = new ConsoleReceiptPrinter(); +basket.PrintReceipt(printer); diff --git a/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs b/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs index 32d3c9d7..5980b4fb 100644 --- a/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs +++ b/exercise.main/ReceiptPrinters/ConsoleReceiptPrinter.cs @@ -11,7 +11,7 @@ public class ConsoleReceiptPrinter : IReceiptPrinter { public void Print(string receipt) { - throw new NotImplementedException(); + Console.WriteLine(receipt); } } } From e1d03257484b72be2f4a375f1f101674a7ef4576 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Tue, 12 Aug 2025 09:26:00 +0200 Subject: [PATCH 10/11] feat: discount receipt --- exercise.main/Basket.cs | 11 +++++++ exercise.main/Interfaces/IDiscountable.cs | 2 ++ exercise.main/Items/Bagel.cs | 13 ++++++++ exercise.main/Items/Coffee.cs | 11 +++++++ exercise.main/Program.cs | 9 +++++- .../ReceiptPrinters/TwilioReceiptPrinter.cs | 31 +++++++++++++++++++ exercise.main/exercise.main.csproj | 4 +++ 7 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 exercise.main/ReceiptPrinters/TwilioReceiptPrinter.cs diff --git a/exercise.main/Basket.cs b/exercise.main/Basket.cs index 524daf18..f26883a8 100644 --- a/exercise.main/Basket.cs +++ b/exercise.main/Basket.cs @@ -131,6 +131,15 @@ public void PrintReceipt(IReceiptPrinter printer) 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 = ""; @@ -149,6 +158,7 @@ private string GetReceiptStartString() startString += $" {DateTime.Now}\n"; startString += "\n"; startString += "----------------------------\n"; + startString += "\n"; return startString; @@ -159,6 +169,7 @@ private string GetReceiptEndString() var totalPrice = GetTotalCost(); var endString = ""; + endString += "\n"; endString += "----------------------------\n"; endString += $"Total {totalPrice.ToString("F2")}\n"; endString += "\n"; diff --git a/exercise.main/Interfaces/IDiscountable.cs b/exercise.main/Interfaces/IDiscountable.cs index 2fa4d233..232a9403 100644 --- a/exercise.main/Interfaces/IDiscountable.cs +++ b/exercise.main/Interfaces/IDiscountable.cs @@ -12,5 +12,7 @@ public interface IDiscountable : IInventoryProduct public bool IsDiscounted { get; set; } public void SetDiscountPrice(Decimal discountPrice); + + public Decimal GetSavedAmount(); } } diff --git a/exercise.main/Items/Bagel.cs b/exercise.main/Items/Bagel.cs index 919d865c..ba870eb5 100644 --- a/exercise.main/Items/Bagel.cs +++ b/exercise.main/Items/Bagel.cs @@ -47,6 +47,19 @@ 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; diff --git a/exercise.main/Items/Coffee.cs b/exercise.main/Items/Coffee.cs index f54976f7..01240f5f 100644 --- a/exercise.main/Items/Coffee.cs +++ b/exercise.main/Items/Coffee.cs @@ -26,6 +26,9 @@ public class Coffee : IInventoryProduct, IDiscountable public Decimal GetFinalPrice() { + if (IsDiscounted) + return _discountedPrice; + return _price; } @@ -34,6 +37,14 @@ 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; diff --git a/exercise.main/Program.cs b/exercise.main/Program.cs index edf43f98..acc08c09 100644 --- a/exercise.main/Program.cs +++ b/exercise.main/Program.cs @@ -3,7 +3,7 @@ using exercise.main.Collections; using exercise.main.ReceiptPrinters; -var basket = new Basket(15); +var basket = new Basket(25); basket.Add(Products.BGLP()); basket.Add(Products.BGLP()); @@ -20,6 +20,13 @@ 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(); 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 + + + + From 2149c406243516cb311101c6ac98b90ee79ebcb7 Mon Sep 17 00:00:00 2001 From: Jakub Mroz Date: Tue, 12 Aug 2025 10:30:01 +0200 Subject: [PATCH 11/11] feat: domain model --- domain-model.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 domain-model.md 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 |