diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj
index 9534619..3eee68a 100644
--- a/LiteCharms.Features/LiteCharms.Features.csproj
+++ b/LiteCharms.Features/LiteCharms.Features.csproj
@@ -61,8 +61,4 @@
-
-
-
-
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs
new file mode 100644
index 0000000..009fccd
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs
@@ -0,0 +1,30 @@
+namespace LiteCharms.Features.ShoppingCarts.Commands;
+
+public class AddItemToShoppingCartCommand : IRequest
+{
+ public Guid ShoppingCartId { get; set; }
+
+ public Guid ProductPriceId { get; set; }
+
+ public int Quantity { get; set; }
+
+ private AddItemToShoppingCartCommand(Guid shoppingCartId, Guid productPriceId, int quantity = 1)
+ {
+ ShoppingCartId = shoppingCartId;
+ ProductPriceId = productPriceId;
+ Quantity = quantity;
+ }
+
+ public static AddItemToShoppingCartCommand Create(Guid shoppingCartId, Guid productPriceId, int quantity = 1)
+ {
+ if (shoppingCartId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId));
+
+ if (productPriceId == Guid.Empty)
+ throw new ArgumentException($"Product item required", nameof(productPriceId));
+
+ if(quantity <= 0) throw new ArgumentException($"Quantity must be at least 1", nameof(quantity));
+
+ return new(shoppingCartId, productPriceId, quantity);
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs
index 3c5098e..fdc7b0e 100644
--- a/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs
+++ b/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs
@@ -1,6 +1,25 @@
namespace LiteCharms.Features.ShoppingCarts.Commands;
-public class CreateShoppingCartCommand : IRequest
+public class CreateShoppingCartCommand : IRequest>
{
+ public Guid? CustomerId { get; set; }
+ public Guid? OrderId { get; set; }
+
+ public Guid? QuoteId { get; set; }
+
+ private CreateShoppingCartCommand(Guid customerId, Guid? orderId = null, Guid? quoteId = null)
+ {
+ CustomerId = customerId;
+ OrderId = orderId;
+ QuoteId = quoteId;
+ }
+
+ public static CreateShoppingCartCommand Create(Guid customerId, Guid? orderId = null, Guid? quoteId = null)
+ {
+ if (customerId == Guid.Empty)
+ throw new ArgumentException($"Customer ID is required", nameof(customerId));
+
+ return new(customerId, orderId, quoteId);
+ }
}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs
new file mode 100644
index 0000000..f535ca4
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs
@@ -0,0 +1,16 @@
+namespace LiteCharms.Features.ShoppingCarts.Commands;
+
+public class EmptyShoppingCartCommand : IRequest
+{
+ public Guid ShoppingCartId { get; set; }
+
+ private EmptyShoppingCartCommand(Guid shoppingCartId) => ShoppingCartId = shoppingCartId;
+
+ public static EmptyShoppingCartCommand Create(Guid shoppingCartId)
+ {
+ if(shoppingCartId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart ID is required.", nameof(shoppingCartId));
+
+ return new(shoppingCartId);
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs
new file mode 100644
index 0000000..3490517
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs
@@ -0,0 +1,40 @@
+using LiteCharms.Infrastructure.Database;
+
+namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers;
+
+public class AddItemToShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler
+{
+ public async ValueTask Handle(AddItemToShoppingCartCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.ProductPrices.AnyAsync(c => c.Id == request.ProductPriceId, cancellationToken))
+ return Result.Fail($"Product item could not be found with id {request.ProductPriceId}");
+
+ var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken);
+
+ if (cart is null)
+ return Result.Fail($"Shopping cart could not be found with id {request.ShoppingCartId}");
+
+ if (cart.ShoppingCartItems?.Any(i => i.ProductPriceId == request.ProductPriceId) == true)
+ return Result.Fail($"Item already in shopping cart with id {request.ShoppingCartId}");
+
+ context.ShoppingCartItems.Add(new Entities.ShoppingCartItem
+ {
+ ShoppingCartId = request.ShoppingCartId,
+ ProductPriceId = request.ProductPriceId,
+ Quantity = request.Quantity
+ });
+
+ return await context.SaveChangesAsync(cancellationToken) > 0
+ ? Result.Ok()
+ : Result.Fail($"Failed to add cart item with id {request.ProductPriceId}");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs
new file mode 100644
index 0000000..c6d17b0
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs
@@ -0,0 +1,32 @@
+using LiteCharms.Infrastructure.Database;
+
+namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers;
+
+public class CreateShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler>
+{
+ public async ValueTask> Handle(CreateShoppingCartCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken))
+ return Result.Fail($"Customer could not be found with id {request.CustomerId}");
+
+ var cart = context.ShoppingCarts.Add(new Entities.ShoppingCart
+ {
+ CustomerId = request.CustomerId,
+ OrderId = request.OrderId,
+ QuoteId = request.QuoteId
+ });
+
+ return await context.SaveChangesAsync(cancellationToken) > 0
+ ? Result.Ok(cart.Entity.Id)
+ : Result.Fail($"Failed to create shopping cart for customer id {request.CustomerId}");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs
new file mode 100644
index 0000000..2787a8c
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs
@@ -0,0 +1,32 @@
+using LiteCharms.Infrastructure.Database;
+
+namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers;
+
+public class EmptyShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler
+{
+ public async ValueTask Handle(EmptyShoppingCartCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken))
+ return Result.Fail($"Shopping could not be found with id {request.ShoppingCartId}");
+
+ if (await context.ShoppingCartItems.CountAsync(i => i.ShoppingCartId == request.ShoppingCartId, cancellationToken) == 0)
+ return Result.Ok();
+
+ var cartItems = await context.ShoppingCartItems.Where(i => i.ShoppingCartId == request.ShoppingCartId).ToListAsync(cancellationToken);
+
+ context.RemoveRange(cartItems);
+
+ return await context.SaveChangesAsync(cancellationToken) > 0
+ ? Result.Ok()
+ : Result.Fail($"Could not empty cart with id {request.ShoppingCartId}");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs
new file mode 100644
index 0000000..1d7944d
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs
@@ -0,0 +1,36 @@
+using LiteCharms.Infrastructure.Database;
+
+namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers;
+
+public class RemoveShoppingCartItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler
+{
+ public async ValueTask Handle(RemoveShoppingCartItemCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.ProductPrices.AnyAsync(c => c.Id == request.ShoppingCartItemId, cancellationToken))
+ return Result.Fail($"Product item could not be found with id {request.ShoppingCartItemId}");
+
+ var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken);
+
+ if (cart is null)
+ return Result.Fail($"Shopping cart item could not be found with id {request.ShoppingCartId}");
+
+ var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.Id == request.ShoppingCartItemId, cancellationToken);
+
+ if (item is null) return Result.Ok();
+
+ context.ShoppingCartItems.Remove(item);
+
+ return await context.SaveChangesAsync(cancellationToken) > 0
+ ? Result.Ok()
+ : Result.Fail($"Failed to remove shopping cart item with id {request.ShoppingCartItemId}");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs
new file mode 100644
index 0000000..b1880ec
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs
@@ -0,0 +1,32 @@
+using LiteCharms.Infrastructure.Database;
+
+namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers;
+
+public class UpdateShoppingCartItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler
+{
+ public async ValueTask Handle(UpdateShoppingCartItemCommand request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken))
+ return Result.Fail($"Shopping could not be found with id {request.ShoppingCartId}");
+
+ var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.ShoppingCartId == request.ShoppingCartId, cancellationToken);
+
+ if(item is null)
+ return Result.Fail($"Shopping cart item could not be found with id {request.ShoppingCartItemId}");
+
+ item.Quantity = request.Quantity;
+
+ return await context.SaveChangesAsync(cancellationToken) > 0
+ ? Result.Ok()
+ : Result.Fail($"Failed to update cart item quntity");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs
new file mode 100644
index 0000000..26706d8
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs
@@ -0,0 +1,25 @@
+namespace LiteCharms.Features.ShoppingCarts.Commands;
+
+public class RemoveShoppingCartItemCommand : IRequest
+{
+ public Guid ShoppingCartId { get; set; }
+
+ public Guid ShoppingCartItemId { get; set; }
+
+ private RemoveShoppingCartItemCommand(Guid shoppingCartId, Guid shoppingCartItemId)
+ {
+ ShoppingCartId = shoppingCartId;
+ ShoppingCartItemId = shoppingCartItemId;
+ }
+
+ public static RemoveShoppingCartItemCommand Create(Guid shoppingCartId, Guid shoppingCartItemId)
+ {
+ if (shoppingCartId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId));
+
+ if (shoppingCartItemId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart item required", nameof(shoppingCartItemId));
+
+ return new(shoppingCartId, shoppingCartItemId);
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs
new file mode 100644
index 0000000..d7cebe0
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs
@@ -0,0 +1,30 @@
+namespace LiteCharms.Features.ShoppingCarts.Commands;
+
+public class UpdateShoppingCartItemCommand : IRequest
+{
+ public Guid ShoppingCartId { get; set; }
+
+ public Guid ShoppingCartItemId { get; set; }
+
+ public int Quantity { get; set; }
+
+ private UpdateShoppingCartItemCommand(Guid shoppingCartId, Guid shoppingCartItemId, int quantity = 1)
+ {
+ ShoppingCartId = shoppingCartId;
+ ShoppingCartItemId = shoppingCartItemId;
+ Quantity = quantity;
+ }
+
+ public static UpdateShoppingCartItemCommand Create(Guid shoppingCartId, Guid shoppingCartItemId, int quantity = 1)
+ {
+ if (shoppingCartId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId));
+
+ if (shoppingCartItemId == Guid.Empty)
+ throw new ArgumentException($"Shopping cart item is required", nameof(shoppingCartItemId));
+
+ if (quantity <= 0) throw new ArgumentException($"Quantity must be at least 1", nameof(quantity));
+
+ return new(shoppingCartId, shoppingCartItemId, quantity);
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs b/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs
new file mode 100644
index 0000000..197d475
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs
@@ -0,0 +1,18 @@
+using LiteCharms.Models;
+
+namespace LiteCharms.Features.ShoppingCarts.Queries;
+
+public class GetShoppingCartItemsQuery : IRequest>
+{
+ public Guid ShoppingCartId { get; set; }
+
+ private GetShoppingCartItemsQuery(Guid shoppingCartId) => ShoppingCartId = shoppingCartId;
+
+ public static GetShoppingCartItemsQuery Create(Guid shoppingCartId)
+ {
+ if (shoppingCartId == Guid.Empty)
+ throw new ArgumentException("Shopping cart id is required", nameof(shoppingCartId));
+
+ return new(shoppingCartId);
+ }
+}
diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs
new file mode 100644
index 0000000..e523c25
--- /dev/null
+++ b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs
@@ -0,0 +1,30 @@
+using LiteCharms.Extensions;
+using LiteCharms.Infrastructure.Database;
+using LiteCharms.Models;
+
+namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers;
+
+public class GetShoppingCartItemsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler>
+{
+ public async ValueTask> Handle(GetShoppingCartItemsQuery request, CancellationToken cancellationToken)
+ {
+ try
+ {
+ using var context = await contextFactory.CreateDbContextAsync(cancellationToken);
+
+ if (!await context.ShoppingCarts.AnyAsync(i => i.Id == request.ShoppingCartId, cancellationToken))
+ return Result.Fail($"Shopping cart could not be found with id {request.ShoppingCartId}");
+
+ var items = await context.ShoppingCartItems.AsNoTracking()
+ .Where(i => i.ShoppingCartId == request.ShoppingCartId).ToArrayAsync(cancellationToken);
+
+ return items?.Length > 0
+ ? Result.Ok(items.Select(i => i.ToModel()).ToArray())
+ : Result.Fail($"Failed to retrieve shopping cart items with id {request.ShoppingCartId}");
+ }
+ catch (Exception ex)
+ {
+ return Result.Fail(new Error(ex.Message).CausedBy(ex));
+ }
+ }
+}