Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -360,4 +360,6 @@ MigrationBackup/
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd

.DS_Store
Binary file added bobs_bagels.drawio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions domain-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
| Class | Method | Scenario | Output / Action |
| --------------- | --------------------------------------------- | -------------------------------------------------------------------- | ------------------ |
| `Basket` | `addProduct(IProduct product)` | Add product when basket has space | Product added |
| | | Add product when basket is full | Error: basket full |
| | `removeProduct(IProduct product)` | Remove product that exists in basket | Product removed |
| | | Remove product not in basket | Error: not found |
| | `isFull()` | Basket is at capacity | `true` |
| | | Basket has free space | `false` |
| | `changeCapacity(int newCapacity)` | Manager changes basket capacity | Capacity updated |
| | `getTotalCost()` | Basket has products | Total price |
| | `getProductCost(String sku)` | Product exists in inventory | Product price |
| | `addFilling(IProduct bagel, Filling filling)` | Filling exists in inventory | Filling added |
| | | Filling not in inventory | Error: invalid |
| | `getFillingCost(Filling filling)` | Filling exists in inventory | Filling price |
| | `validateProductExists(IProduct product)` | Product exists in inventory | `true` |
| | | Product not in inventory | `false` |
| `Inventory` | `hasProduct(String sku)` | SKU exists | `true` |
| | | SKU does not exist | `false` |
| | `getProductPrice(String sku)` | SKU exists | Price (num) |
| | | SKU not found | Error |
| | `getFillingPrice(String sku)` | SKU exists and is filling | Price (num) |
| `IProducts` | — | Represents bagels, coffees, and fillings (SKU, name, variant, price) | — |
84 changes: 84 additions & 0 deletions exercise.main/Basket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System.Text;
using exercise.main.Interfaces;
using exercise.main.Products;

namespace exercise.main;

public class Basket(int maxSize, Inventory productSelection)
{
private readonly Inventory _inventory = productSelection;
private readonly List<IProduct> _basket = [];
private int _basketMaxSize = maxSize;

public bool AddProduct(IProduct product)
{
if (!_inventory.HasProduct(product)) return false;
if (_basket.Count >= _basketMaxSize) throw new OverflowException("Basket is full!");
_basket.Add(product);
return true;
}

public bool ApplyDiscount(IDiscount discount) => discount.ApplyDiscount(this);

public int Count() => _basket.Count;

public bool AddFillingToBagel(Bagel bagel, Filling filling)
{
var b = _basket.Find(p => p.Id == bagel.Id);
if (b is Bagel bagelItem)
{
bagelItem.AddFilling(filling);
return true;
}
return false;
}

public decimal GetTotalCost() => _basket.Sum(p => p.GetPrice());

public decimal GetCostOfSku(string sku) =>
_basket.Where(p => p.Sku == sku).Sum(p => p.GetPrice());

public void ChangeCapacity(int newCapacity) => _basketMaxSize = newCapacity;

public bool RemoveProduct(IProduct product)
{
if (!_basket.Remove(product)) throw new KeyNotFoundException("Product does not exist in basket!");
return true;
}

public bool RemoveAllProduct(string sku)
{
if (!_basket.Any(p => p.Sku == sku)) throw new KeyNotFoundException("Product does not exist in basket!");
_basket.RemoveAll(p => p.Sku == sku);
return true;
}

public bool IsFull() => _basket.Count >= _basketMaxSize;

public bool ValidateProductExists(IProduct product) => _basket.Contains(product);

public List<IProduct> GetBasket() => _basket;

public override string ToString()
{
StringBuilder s = new();

s.Append("\n~~~ Bob's Bagels ~~~");
s.Append($"\n{DateTime.Now}");
s.Append("\n------");

_basket
.GroupBy(p => p.Sku)
.ToList()
.ForEach(g =>
s.Append($"\n{g.First().Name} {g.Count()} {g.Sum(p => p.GetPrice()):C}")
);

s.Append("\n------");
s.Append($"\nTotal: {this.GetTotalCost()}");
s.Append("\nThank you");
s.Append("\nfor your order!");

return s.ToString();
}
}
12 changes: 12 additions & 0 deletions exercise.main/Interfaces/IDiscount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace exercise.main.Interfaces;

public interface IDiscount
{
Guid Id { get; }
string Name { get; set; }
string Description { get; set; }

bool ApplyDiscount(Basket basket);
}
12 changes: 12 additions & 0 deletions exercise.main/Interfaces/IProduct.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace exercise.main.Interfaces;

public interface IProduct
{
string Sku { get; set; }
string Name { get; set; }
string Variant { get; set; }
Guid Id { get; }
decimal GetPrice();
bool ApplyDiscount(decimal discount);
bool IsDiscounted();
}
36 changes: 36 additions & 0 deletions exercise.main/Inventory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using exercise.main.Interfaces;

namespace exercise.main;

public class Inventory
{
private List<IProduct> _products = [];

public void AddToInventory(IProduct product)
{
_products.Add(product);
}

public decimal GetProductPrice(string sku)
{
IProduct p = (IProduct)_products.Where(p => p.Sku.Equals(sku));

if (p == null) throw new KeyNotFoundException($"SKU '{sku}', does not exist in inventory!");

return p.GetPrice();
}

public bool HasSku(string sku)
{
IProduct? p = _products.FirstOrDefault(p => p.Sku.Equals(sku));
return p != null;
}

public bool HasProduct(IProduct product)
{
IProduct? p = _products.FirstOrDefault(p => p.Sku.Equals(product.Sku));
if (p == null || !p.Name.Equals(product.Name) || !p.Variant.Equals(product.Variant)) return false;
return true;
}
}
46 changes: 46 additions & 0 deletions exercise.main/Objects/Discounts/CoffeeBagelDiscount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using exercise.main.Interfaces;
using exercise.main.Products;

namespace exercise.main.Objects.Discounts;

public class CoffeeBagelsDiscount : IDiscount
{
public Guid Id { get; } = Guid.NewGuid();
public string Name { get; set; } = "The Coffe & Bagel Discount!";
public string Description { get; set; } = "Buy a coffee and a bagle for just 1.25!";

public bool ApplyDiscount(Basket basket)
{
bool isSuccess = false;

var availableItems = basket
.GetBasket()
.Where(p => !p.IsDiscounted())
.ToList();

var bagel = availableItems.OfType<Bagel>().FirstOrDefault();
var coffee = availableItems.OfType<Coffee>().FirstOrDefault();

if (bagel != null && coffee != null)
{
decimal totalComboPrice = Math.Max(bagel.GetPrice() + coffee.GetPrice() - 1.25m, 0);
decimal bagelDiscount = bagel.GetPrice();
decimal coffeeDiscount = coffee.GetPrice();

if (totalComboPrice != 0)
{
bagelDiscount = bagel.GetPrice() - (totalComboPrice / 2);
coffeeDiscount = coffee.GetPrice() - (totalComboPrice / 2);
}

bagel.ApplyDiscount(bagel.GetPrice()-bagelDiscount);
coffee.ApplyDiscount(coffee.GetPrice()-coffeeDiscount);

isSuccess = true;
}

return isSuccess;
}

}
39 changes: 39 additions & 0 deletions exercise.main/Objects/Discounts/SixBagelsDiscount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using exercise.main.Interfaces;
using exercise.main.Products;

namespace exercise.main.Objects.Discounts;

public class SixBagelsDiscount : IDiscount
{
public Guid Id { get; } = Guid.NewGuid();
public string Name { get; set; } = "The Six Bagel Discount!";
public string Description { get; set; } = "Get a discount when you buy 6 bagels. 6 of the same bagel for 2.49! What a deal!";

public bool ApplyDiscount(Basket basket)
{
bool isSuccess = false;
decimal _discount = 2.49m / 6;

var groupedBySku = basket
.GetBasket()
.Where(p => !p.IsDiscounted())
.GroupBy(p => p.Sku)
.Where(g => g.Count() >= 6)
.ToList();

foreach (var group in groupedBySku)
{
if (group.First() is Bagel)
{
isSuccess = true;
foreach (var b in group.Take(6))
{
b.ApplyDiscount(b.GetPrice() - _discount);
}
}
}

return isSuccess;
}
}
39 changes: 39 additions & 0 deletions exercise.main/Objects/Discounts/TwelveBagelsDiscount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System;
using exercise.main.Interfaces;
using exercise.main.Products;

namespace exercise.main.Objects.Discounts;

public class TwelveBagelsDiscount : IDiscount
{
public Guid Id { get; } = Guid.NewGuid();
public string Name { get; set; } = "The Twelve Bagel Discount!";
public string Description { get; set; } = "Get a discount when you buy 12 bagels. 12 of the same bagel for 3.99! What a deal!";

public bool ApplyDiscount(Basket basket)
{
bool isSuccess = false;
decimal _discount = 3.99m / 12;

var groupedBySku = basket
.GetBasket()
.Where(p => !p.IsDiscounted())
.GroupBy(p => p.Sku)
.Where(g => g.Count() >= 12)
.ToList();

foreach (var group in groupedBySku)
{
if (group.First() is Bagel)
{
isSuccess = true;
foreach (var b in group.Take(12))
{
b.ApplyDiscount(b.GetPrice() - _discount);
}
}
}

return isSuccess;
}
}
57 changes: 57 additions & 0 deletions exercise.main/Objects/Products/Bagel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using exercise.main.Interfaces;

namespace exercise.main.Products;

public class Bagel : IProduct
{
private List<Filling> _fillings = [];
private decimal _basePrice;
private bool _isDiscounted = false;
public Guid Id { get; } = Guid.NewGuid();

public Bagel(string ProductSku, string ProductName, string ProductVariant, decimal ProductBasePrice)
{
this.Sku = ProductSku;
this.Name = ProductName;
this.Variant = ProductVariant;
this._basePrice = ProductBasePrice;
}

public string Sku { get; set; }
public string Name { get; set; }
public string Variant { get; set; }

public decimal GetPrice()
{
if (_fillings.Count == 0) return _basePrice;

return _basePrice + _fillings.Sum(f => f.GetPrice());
}

public bool ApplyDiscount(decimal discount)
{
if (_isDiscounted) return false;

_basePrice = Math.Max(_basePrice - discount, 0);

_isDiscounted = true;

return true;
}

public bool IsDiscounted()
{
return _isDiscounted;
}

public List<Filling> GetFillings()
{
return _fillings;
}

public void AddFilling(Filling BagelFilling)
{
_fillings.Add(BagelFilling);
}
}
Loading