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
50 changes: 50 additions & 0 deletions domain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Domain

## User stories
1. Public: So I can order a bagel before work, I'd like to add a specific type of bagel to my basket.
2. Public: So I can change my order, I'd like to remove a bagel from my basket.
3. Public: So that I can not overfill my small bagel basket I'd like to know when my basket is full when I try adding an item beyond my basket capacity.
4. Public: So that I can maintain my sanity I'd like to know if I try to remove an item that doesn't exist in my basket.
5. Customer: So I know how much money I need, I'd like to know the total cost of items in my basket.
6. Customer: So I know what the damage will be, I'd like to know the cost of a bagel before I add it to my basket.
7. Customer: So I can shake things up a bit, I'd like to be able to choose fillings for my bagel.
8. Customer: So I don't over-spend, I'd like to know the cost of each filling before I add it to my bagel order.
9. Manager: So we don't get any weird requests, I want customers to only be able to order things that we stock in our inventory.
10. Manager: So that I can expand my business, I’d like to change the capacity of baskets.

## Requirements
* Add bagel to basket
* Remove bagel from basket
* Basket capacity
* Feedback on full basket
* Error on remove non-existing item
* See total cost of basket
* See cost of bagel before adding to basket
* Allow for choosing filling of bagel
* See cost of filling before adding to bagel
* Only allow for ordering items in stock
* Allow for changing basket capacity for admins


### Extensions:

1. Allow for special discounts: 3 for 2 and such
2. Add functionality for receipts to be printed
3. Support discounts in the receipts
4. Send text message confirmation
5. Order by text message
6. See text message history

## Classes

* StoreItem
* Basket
* Store
* Receipt
* User?
* Discount
* IRepository
* ListRepository

** Instead of making a table, I made empty classes and function stubs before creating any code. **

56 changes: 56 additions & 0 deletions exercise.main/Basket.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace exercise.core;

public class Basket
{
private List<StoreItem> items = new List<StoreItem>();
public required int Capacity { get; set; }

public List<StoreItem> GetItems()
{
return this.items;
}

public Receipt Purchase(DiscountContainer discounts)
{
var discounted = discounts.ApplyDiscounts(items);
this.items = new List<StoreItem>();
return new Receipt(discounted);
}

public double GetTotalPrice(DiscountContainer discounts)
{
var discounted = discounts.ApplyDiscounts(items);
return discounted.Select(i => i.GetPrice()).Sum();
}

public bool AddItem(StoreItem item)
{
if (this.items.Count >= this.Capacity)
{
return false;
}
this.items.Add(item);
return true;
}

public bool RemoveItem(StoreItem storeItem)
{
var found = this.items.Find(i => i.Equals(storeItem));
if (found == null)
{
return false;
}
this.items.Remove(found);
return true;
}

public bool UpdateCapacity(int newCapacity)
{
if (newCapacity < this.items.Count)
{
return false;
}
this.Capacity = newCapacity;
return true;
}
}
137 changes: 137 additions & 0 deletions exercise.main/Discount.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
namespace exercise.core;

public class DiscountContainer
{
public List<Discount> discounts = new List<Discount>();

public void AddDiscount(Discount discount)
{
this.discounts.Add(discount);
this.discounts = this.discounts.OrderByDescending(disc => disc.priority).ToList();
}

public List<StoreItem> ApplyDiscounts(List<StoreItem> items)
{
List<StoreItem> discounted = new List<StoreItem>();
List<StoreItem> nonDiscounted = new List<StoreItem>(items);

foreach (Discount disc in this.discounts)
{
bool applicable = true;
foreach ((Predicate<StoreItem> pred, int amount) in disc.DiscountRequirement)
{
if (nonDiscounted.Where(it => pred(it)).Count() < amount)
{
applicable = false;
break;
}
}

if (applicable)
{
List<StoreItem> toBundle = new List<StoreItem>();
foreach ((Predicate<StoreItem> pred, int amount) in disc.DiscountRequirement)
{
for (int i = 0; i < amount; i++)
{
var toDiscount = nonDiscounted.Find(pred);
if (toDiscount == null)
{
throw new Exception("oops");
}
nonDiscounted.Remove(toDiscount);
toBundle.Add(toDiscount);
}
}
discounted.Add(new DiscountBundle(toBundle, disc.newPrice));
}
}
return nonDiscounted.Concat(discounted).ToList();
}
}

public class Discount
{
public required List<(
Predicate<StoreItem> requiredItem,
int requiredAmount
)> DiscountRequirement { get; init; }
public required double newPrice { get; init; }
public required int priority { get; init; }
}

public class DiscountBundle : StoreItem
{
private List<StoreItem> _storeItems;
private double _oldPrice;

public DiscountBundle(
string code,
string name,
string variant,
double newPrice,
double oldPrice
)
: base(code, name, variant, newPrice)
{
this._storeItems = new List<StoreItem>();
this._oldPrice = oldPrice;
}

public DiscountBundle(List<StoreItem> items, double newPrice)
: base("DISC", "Discount", "", newPrice)
{
this._storeItems = items;
this._oldPrice = items.Sum(it => it.GetPrice());
}

public override double GetPrice()
{
var nonDiscounted = 0.0;
foreach (StoreItem item in this._storeItems)
{
foreach (StoreItem flat in item.GetItemsFlattened())
{
if (flat is NonDiscountable)
{
nonDiscounted += flat.GetPrice();
}
}
}
return this._price + nonDiscounted;
}

public void AddItem(StoreItem storeItem)
{
this._storeItems.Add(storeItem);
}

public void SetNewPrice(double newPrice)
{
this._price = newPrice;
}

public override IReadOnlyCollection<StoreItem> GetItemsFlattened()
{
return base.GetItemsFlattened();
}

public double GetSavedAmount()
{
return this._oldPrice - this._price;
}

public override string ToString()
{
var sb = new System.Text.StringBuilder();
sb.AppendLine();

foreach (StoreItem discounted in this._storeItems)
{
sb.Append(discounted.ToString());
}
sb.AppendLine($" - £({this.GetSavedAmount():F2})");
sb.AppendLine();
return sb.ToString();
}
}
26 changes: 25 additions & 1 deletion exercise.main/Program.cs
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
// See https://aka.ms/new-console-template for more information
Console.WriteLine("Hello, World!");
using exercise.core;

IRepository repo = LocalRepository.Default();
Store store = new Store(repo);
User bob = new User { UserId = "bob", priv = Privilege.Admin };
store.AddUser(bob);
store.setActiveUser(bob);
store.ModifyCartCapacity(bob, 10);

for (int i = 0; i < 8; i++)
{
var bagel = LocalRepository.Default().getRegisteredItems()[0];
if (bagel is Bagel bg)
{
var filling = LocalRepository.Default().getRegisteredItems()[9];
if (filling is BagelFilling f)
{
bg.AddFilling(f);
}
}
store.AddToCart(bagel);
}

var r = store.Checkout();
System.Console.WriteLine(r?.GetReceiptText());
49 changes: 49 additions & 0 deletions exercise.main/Receipt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace exercise.core;

public class Receipt
{
private List<StoreItem> _purchasedItems = new List<StoreItem>();

public Receipt(List<StoreItem> items)
{
this._purchasedItems = items;
}

public string GetReceiptText()
{
var rb = new System.Text.StringBuilder();
rb.AppendLine(" ~~~ Bob's Bagels ~~~");
rb.AppendLine();
rb.AppendLine(DateTime.Now.ToString());
rb.AppendLine();
rb.AppendLine("----------------------------");
rb.AppendLine();
this._purchasedItems.ForEach(item => rb.AppendLine(item.ToString()));
rb.AppendLine();
rb.AppendLine("----------------------------");
rb.AppendLine(
$" Total cost £{this._purchasedItems.Select(i => i.GetPrice()).Sum():F2}"
);
rb.AppendLine();

var savedAmount = 0.0;
foreach (StoreItem storeItem in this._purchasedItems)
{
if (storeItem is DiscountBundle discounted)
{
savedAmount += discounted.GetSavedAmount();
}
}
rb.AppendLine($"You saved a total of {savedAmount:F2}");
rb.AppendLine("on this trip");

rb.AppendLine("Thank you");
rb.AppendLine("for your order!");
return rb.ToString();
}

public void PrintReceipt()
{
System.Console.Write(this.GetReceiptText());
}
}
Loading