diff --git a/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj b/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj index 86652ed..aeaaf0b 100644 --- a/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj +++ b/LiteCharms.Features.MidrandBooks.Seed/LiteCharms.Features.MidrandBooks.Seed.csproj @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -30,11 +30,11 @@ - - - - - + + + + + @@ -47,9 +47,9 @@ - - - + + + @@ -58,12 +58,12 @@ - - + + - + @@ -75,13 +75,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -116,8 +116,8 @@ - - + + diff --git a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj index a8bef3a..c2f0ac7 100644 --- a/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj +++ b/LiteCharms.Features.MidrandBooks/LiteCharms.Features.MidrandBooks.csproj @@ -36,7 +36,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -50,11 +50,11 @@ - - - - - + + + + + @@ -67,9 +67,9 @@ - - - + + + @@ -78,12 +78,12 @@ - - + + - + @@ -95,13 +95,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -136,8 +136,8 @@ - - + + diff --git a/LiteCharms.Features.MidrandBooks/Payments/CartService.cs b/LiteCharms.Features.MidrandBooks/Payments/CartService.cs new file mode 100644 index 0000000..64d55bd --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Payments/CartService.cs @@ -0,0 +1,154 @@ +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.Browser; +using LiteCharms.Features.Hasher; +using LiteCharms.Features.MidrandBooks.Authors.Models; +using LiteCharms.Features.MidrandBooks.Payments.Models; +using LiteCharms.Features.MidrandBooks.Products.Models; + +namespace LiteCharms.Features.MidrandBooks.Payments; + +public sealed class CartService(LocalStorageService localStorage) : IService +{ + private readonly string CartStorageKey = HashService.ToMd5Hash(nameof(Cart)).Value; + + public Cart ShoppingCart { get; private set; } = new(); + + public event Action? OnCartChanged; + + public static Func GetCartItemQuantity = (shoppingCart, productPriceId) => + shoppingCart.Items.FirstOrDefault(p => p.Price!.Id == productPriceId)?.Quantity ?? 1; + + public Cart GetCart() => ShoppingCart; + + public void NotifyStateChanged() => OnCartChanged?.Invoke(); + + public async Task LoadCartFromStorageAsync() + { + var loadResult = await localStorage.GetAsync(CartStorageKey); + + if (loadResult.IsFailed) await localStorage.SaveAsync(CartStorageKey, ShoppingCart); + + if (loadResult.IsSuccess) ShoppingCart = loadResult.Value; + + NotifyStateChanged(); + } + + public async Task SaveCartToStorageAsync() => await localStorage.SaveAsync(CartStorageKey, ShoppingCart); + + public void AddItem(ProductPrice productPrice, Product product, Author author) + { + var itemExists = false; + + for (var i = 0; i < ShoppingCart.Items.Count; i++) + { + if (ShoppingCart.Items[i].Price!.Id == productPrice.Id) + { + ShoppingCart.Items[i].Quantity++; + ShoppingCart.Items[i].Amount += productPrice.Amount; + + itemExists = true; + + break; + } + } + + if (!itemExists) + ShoppingCart.Items.Add(new CartItem + { + Product = product, + Author = author, + Price = productPrice, + Amount = productPrice.Amount, + Quantity = 1, + }); + + CalculateTotalPrice(); + NotifyStateChanged(); + } + + public void UpdateQuantity(long productPriceId, int delta) + { + for (var i = 0; i < ShoppingCart.Items.Count; i++) + { + if (ShoppingCart.Items[i].Price!.Id == productPriceId) + { + var oldQuantity = ShoppingCart.Items[i].Quantity; + var pricePerUnit = ShoppingCart.Items[i].Price!.Amount; + + ShoppingCart.Items[i].Quantity += delta; + ShoppingCart.Items[i].Amount = pricePerUnit * ShoppingCart.Items[i].Quantity; + break; + } + } + + CalculateTotalPrice(); + NotifyStateChanged(); + } + + public void RemoveOneItem(long productPriceId) + { + for (var i = 0; i < ShoppingCart.Items.Count; i++) + { + if (ShoppingCart.Items[i].Price!.Id == productPriceId) + { + if (ShoppingCart.Items[i].Quantity <= 1) + { + ShoppingCart.Items.Remove(ShoppingCart.Items[i]); + + break; + } + else + { + ShoppingCart.Items[i].Quantity--; + ShoppingCart.Items[i].Amount -= ShoppingCart.Items[i].Price!.Amount; + } + + break; + } + } + + CalculateTotalPrice(); + NotifyStateChanged(); + } + + public void RemoveAllSameItem(long productPriceId) + { + if (ShoppingCart.Items.Count == 0) return; + + var item = ShoppingCart.Items.FirstOrDefault(i => i.Price?.Id == productPriceId); + + if (item is not null) ShoppingCart.Items.Remove(item); + + CalculateTotalPrice(); + NotifyStateChanged(); + } + + public void Clear() + { + if(ShoppingCart.CustomerId is not null || ShoppingCart.OrderId is not null) + { + ShoppingCart.TotalAmount = 0; + ShoppingCart.TotalVat = 0; + ShoppingCart.Items.Clear(); + + return; + } + + ShoppingCart = new Cart(); + + NotifyStateChanged(); + } + + public decimal CalculateTotalPrice() + { + if (ShoppingCart.Items.Count == 0) return 0; + + var gross = ShoppingCart.Items.Sum(i => i.Amount); + + if (!ShoppingCart.IsVatInclusive) ShoppingCart.TotalVat = gross * ShoppingCart.VatRate; + + ShoppingCart.TotalAmount = gross + ShoppingCart.TotalVat; + + return ShoppingCart.TotalAmount; + } +} diff --git a/LiteCharms.Features.MidrandBooks/Payments/Models/Cart.cs b/LiteCharms.Features.MidrandBooks/Payments/Models/Cart.cs new file mode 100644 index 0000000..3af0292 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Payments/Models/Cart.cs @@ -0,0 +1,18 @@ +namespace LiteCharms.Features.MidrandBooks.Payments.Models; + +public sealed class Cart +{ + public long? CustomerId { get; set; } + + public long? OrderId { get; set; } + + public decimal TotalAmount { get; set; } + + public decimal TotalVat { get; set; } + + public decimal VatRate { get; set; } = 0.15m; + + public bool IsVatInclusive { get; set; } = true; + + public IList Items { get; set; } = []; +} diff --git a/LiteCharms.Features.MidrandBooks/Payments/Models/CartItem.cs b/LiteCharms.Features.MidrandBooks/Payments/Models/CartItem.cs new file mode 100644 index 0000000..a656420 --- /dev/null +++ b/LiteCharms.Features.MidrandBooks/Payments/Models/CartItem.cs @@ -0,0 +1,17 @@ +using LiteCharms.Features.MidrandBooks.Authors.Models; +using LiteCharms.Features.MidrandBooks.Products.Models; + +namespace LiteCharms.Features.MidrandBooks.Payments.Models; + +public sealed class CartItem +{ + public Author? Author { get; set; } + + public Product? Product { get; set; } + + public ProductPrice? Price { get; set; } + + public int Quantity { get; set; } + + public decimal Amount { get; set; } +} diff --git a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj index 5f01765..2b3ad94 100644 --- a/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj +++ b/LiteCharms.Features.TechShop/LiteCharms.Features.TechShop.csproj @@ -36,7 +36,7 @@ - + @@ -50,11 +50,11 @@ - - - - - + + + + + @@ -67,9 +67,9 @@ - - - + + + @@ -78,12 +78,12 @@ - - + + - + @@ -95,13 +95,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -136,8 +136,8 @@ - - + + diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index fc6cc2f..b2374c2 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -35,8 +35,8 @@ - - + + @@ -56,8 +56,8 @@ - - + + @@ -71,7 +71,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -85,11 +85,11 @@ - - - - - + + + + + @@ -102,9 +102,9 @@ - - - + + + @@ -113,12 +113,12 @@ - - + + - + @@ -130,13 +130,13 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -171,8 +171,8 @@ - - + +