From 83f51c6a2390cb49b4eb9f190e569fd321a47448 Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Tue, 5 May 2026 23:59:31 +0200 Subject: [PATCH] Added notifications Added shopping cart and items Added quotes Refactored relatinoships Migrated changes Refactored cqrs commands and queries Refactored mappings --- .../NotificationConfiguration.cs | 3 +- .../Configuration/OrderConfiguration.cs | 9 +- .../Configuration/QuoteConfiguration.cs | 23 + .../ShoppingCartConfiguration.cs | 31 + .../ShoppingCartItemConfiguration.cs | 25 + LiteCharms.Entities/Customer.cs | 4 + LiteCharms.Entities/Order.cs | 4 +- LiteCharms.Entities/Quote.cs | 13 + LiteCharms.Entities/ShoppingCart.cs | 15 + LiteCharms.Entities/ShoppingCartItem.cs | 8 + LiteCharms.Extensions/EntityModeMappers.cs | 52 +- .../Customers/Queries/GetCustomerQuery.cs | 18 + .../Handlers/GetCustomerQueryHandler.cs | 26 + .../LiteCharms.Features.csproj | 4 + .../Commands/CreateNotificationCommand.cs | 63 ++ .../CreateNotificationCommandHandler.cs | 35 + .../UpdateNotificationCommandHandler.cs | 29 + .../Commands/UpdateNotificationCommand.cs | 22 + .../Queries/GetNotificationQuery.cs | 18 + .../Handlers/GetNotificationQueryHandler.cs | 26 + .../Handlers/GetNotificationsQueryHandler.cs | 2 +- .../Orders/Commands/CreateOrderCommand.cs | 17 +- .../Handlers/CreateOrderCommandHandler.cs | 14 +- ....cs => UpdateOrderStatusCommandHandler.cs} | 4 +- ...Command.cs => UpdateOrderStatusCommand.cs} | 6 +- .../Queries/GetCustomerOrdersQuery.cs | 2 +- .../Handlers/GetCustomerOrdersQueryHandler.cs | 5 +- .../Handlers/GetProductPriceQueryHandler.cs | 3 + .../Commands/AssignQuoteToOrderCommand.cs | 25 + .../AssignQuoteToShoppingCartCommand.cs | 25 + .../AssignQuoteToOrderCommandHandler.cs | 35 + ...AssignQuoteToShoppingCartCommandHandler.cs | 32 + .../UpdateQuoteStatusCommandHandler.cs | 29 + .../Commands/UpdateQuoteStatusCommand.cs | 24 + .../Quotes/Queries/GetCustomerQuotesQuery.cs | 18 + .../Quotes/Queries/GetQuoteQuery.cs | 18 + .../Quotes/Queries/GetQuotesQuery.cs | 30 + .../Handlers/GetCustomerQuotesQueryHandler.cs | 31 + .../Queries/Handlers/GetQuoteQueryHandler.cs | 26 + .../Queries/Handlers/GetQuotesHandler.cs | 33 + .../Handlers/RefundCustomerCommandHandler.cs | 2 +- .../UpdateOrderRefundCommandHandler.cs | 30 + .../Commands/RefundCustomerCommand.cs | 2 +- .../Commands/UpdateOrderRefundCommand.cs | 28 + .../Queries/GetCustomerRefundsQuery.cs | 2 +- .../Refunds/Queries/GetRefundQuery.cs | 18 + .../GetCustomerRefundsQueryHandler.cs | 3 +- .../Queries/Handlers/GetRefundQueryHandler.cs | 26 + .../Commands/CreateShoppingCartCommand.cs | 6 + .../Queries/GetCustomerShoppingCartsQuery.cs | 18 + .../Queries/GetShoppingCartQuery.cs | 18 + .../GetCustomerShoppingCartsQueryHandler.cs | 30 + .../Handlers/GetShoppingCartQueryHandler.cs | 26 + .../Database/LeadGeneratorDbContext.cs | 6 + ...60505123745_AddedNotifications.Designer.cs | 409 ++++++++++++ .../20260505123745_AddedNotifications.cs | 43 ++ ...ProcessedColumnToNotifications.Designer.cs | 416 ++++++++++++ ...135_AddedProcessedColumnToNotifications.cs | 47 ++ ...oppingCartalteredOrderCustomer.Designer.cs | 604 ++++++++++++++++++ ...edQuoteShoppingCartalteredOrderCustomer.cs | 227 +++++++ .../LeadGeneratorDbContextModelSnapshot.cs | 258 +++++++- LiteCharms.Models/Enums.cs | 9 + LiteCharms.Models/Notification.cs | 2 + LiteCharms.Models/Order.cs | 4 +- LiteCharms.Models/Quote.cs | 20 + LiteCharms.Models/ShoppingCart.cs | 16 + LiteCharms.Models/ShoppingCartItem.cs | 16 + 67 files changed, 3051 insertions(+), 42 deletions(-) create mode 100644 LiteCharms.Entities/Configuration/QuoteConfiguration.cs create mode 100644 LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs create mode 100644 LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs create mode 100644 LiteCharms.Entities/Quote.cs create mode 100644 LiteCharms.Entities/ShoppingCart.cs create mode 100644 LiteCharms.Entities/ShoppingCartItem.cs create mode 100644 LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs create mode 100644 LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs create mode 100644 LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs create mode 100644 LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs create mode 100644 LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs create mode 100644 LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs create mode 100644 LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs create mode 100644 LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs rename LiteCharms.Features/Orders/Commands/Handlers/{UpdateOrderCommandHandler.cs => UpdateOrderStatusCommandHandler.cs} (75%) rename LiteCharms.Features/Orders/Commands/{UpdateOrderCommand.cs => UpdateOrderStatusCommand.cs} (71%) rename LiteCharms.Features/{Customers => Orders}/Queries/GetCustomerOrdersQuery.cs (90%) rename LiteCharms.Features/{Customers => Orders}/Queries/Handlers/GetCustomerOrdersQueryHandler.cs (78%) create mode 100644 LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs create mode 100644 LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs create mode 100644 LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs create mode 100644 LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs create mode 100644 LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs create mode 100644 LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs create mode 100644 LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs create mode 100644 LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs create mode 100644 LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs create mode 100644 LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs create mode 100644 LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs create mode 100644 LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs rename LiteCharms.Features/{Customers => Refunds}/Commands/Handlers/RefundCustomerCommandHandler.cs (96%) create mode 100644 LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs rename LiteCharms.Features/{Customers => Refunds}/Commands/RefundCustomerCommand.cs (95%) create mode 100644 LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs rename LiteCharms.Features/{Customers => Refunds}/Queries/GetCustomerRefundsQuery.cs (90%) create mode 100644 LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs rename LiteCharms.Features/{Customers => Refunds}/Queries/Handlers/GetCustomerRefundsQueryHandler.cs (93%) create mode 100644 LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs create mode 100644 LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs create mode 100644 LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs create mode 100644 LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs create mode 100644 LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs create mode 100644 LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.Designer.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.Designer.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.Designer.cs create mode 100644 LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.cs create mode 100644 LiteCharms.Models/Quote.cs create mode 100644 LiteCharms.Models/ShoppingCart.cs create mode 100644 LiteCharms.Models/ShoppingCartItem.cs diff --git a/LiteCharms.Entities/Configuration/NotificationConfiguration.cs b/LiteCharms.Entities/Configuration/NotificationConfiguration.cs index 7d118f5..c5879f9 100644 --- a/LiteCharms.Entities/Configuration/NotificationConfiguration.cs +++ b/LiteCharms.Entities/Configuration/NotificationConfiguration.cs @@ -16,6 +16,7 @@ public class NotificationConfiguration : IEntityTypeConfiguration builder.Property(f => f.PlatformAddress).IsRequired(); builder.Property(f => f.CorrelationId).IsRequired(); builder.Property(f => f.CorrelationIdType).IsRequired(); - builder.Property(f => f.IsInternal).IsRequired(); + builder.Property(f => f.IsInternal).HasDefaultValue(true); + builder.Property(f => f.Processed).HasDefaultValue(false); } } \ No newline at end of file diff --git a/LiteCharms.Entities/Configuration/OrderConfiguration.cs b/LiteCharms.Entities/Configuration/OrderConfiguration.cs index 5e51f70..5db15cd 100644 --- a/LiteCharms.Entities/Configuration/OrderConfiguration.cs +++ b/LiteCharms.Entities/Configuration/OrderConfiguration.cs @@ -10,14 +10,15 @@ public class OrderConfiguration : IEntityTypeConfiguration builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd(); builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); builder.Property(f => f.CustomerId).IsRequired(); + builder.Property(f => f.QuoteId).IsRequired(false); builder.Property(f => f.RefundId).IsRequired(false); - builder.Property(f => f.ProductPriceId).IsRequired(); + builder.Property(f => f.ShoppingCartId).IsRequired(); builder.Property(f => f.Status).HasConversion().IsRequired(); builder.Property(f => f.Notes).HasColumnType("jsonb").IsRequired(false); - builder.HasOne(f => f.ProductPrice) - .WithMany() - .HasForeignKey(f => f.ProductPriceId) + builder.HasOne(f => f.Quote) + .WithOne(f => f.Order) + .HasForeignKey(f => f.QuoteId) .OnDelete(DeleteBehavior.Restrict); builder.HasOne(f => f.Customer) diff --git a/LiteCharms.Entities/Configuration/QuoteConfiguration.cs b/LiteCharms.Entities/Configuration/QuoteConfiguration.cs new file mode 100644 index 0000000..37125ae --- /dev/null +++ b/LiteCharms.Entities/Configuration/QuoteConfiguration.cs @@ -0,0 +1,23 @@ +namespace LiteCharms.Entities.Configuration; + +public class QuoteConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Quote)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); + builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate(); + builder.Property(f => f.ExpiredAt).IsRequired(false); + builder.Property(f => f.CustomerId).IsRequired(); + builder.Property(f => f.Status).IsRequired(); + builder.Property(f => f.ShoppingCartId).IsRequired(); + builder.Property(f => f.Reason).IsRequired(false); + + builder.HasOne(f => f.Customer) + .WithMany() + .HasForeignKey(f => f.CustomerId) + .OnDelete(DeleteBehavior.Cascade); + } +} diff --git a/LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs b/LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs new file mode 100644 index 0000000..3a1926b --- /dev/null +++ b/LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs @@ -0,0 +1,31 @@ +namespace LiteCharms.Entities.Configuration; + +public class ShoppingCartConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(ShoppingCart)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); + builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate(); + builder.Property(f => f.CustomerId).IsRequired(false); + builder.Property(f => f.OrderId).IsRequired(false); + builder.Property(f => f.QuoteId).IsRequired(false); + + builder.HasOne(f => f.Customer) + .WithMany(c => c.ShoppingCarts) + .HasForeignKey(f => f.CustomerId) + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Order) + .WithOne(o => o.ShoppingCart) + .HasForeignKey(o => o.ShoppingCartId) + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.Quote) + .WithOne(o => o.ShoppingCart) + .HasForeignKey(o => o.ShoppingCartId) + .OnDelete(DeleteBehavior.NoAction); + } +} diff --git a/LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs b/LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs new file mode 100644 index 0000000..00fc2ab --- /dev/null +++ b/LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs @@ -0,0 +1,25 @@ +namespace LiteCharms.Entities.Configuration; + +public class ShoppingCartItemConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(ShoppingCartItem)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); + builder.Property(f => f.UpdatedAt).IsRequired().ValueGeneratedOnAddOrUpdate(); + builder.Property(f => f.Quantity).IsRequired().HasDefaultValue(1); + builder.Property(f => f.ProductPriceId).IsRequired(); + + builder.HasOne(f => f.ProductPrice) + .WithMany() + .HasForeignKey(f => f.ProductPriceId) + .OnDelete(DeleteBehavior.NoAction); + + builder.HasOne(f => f.ShoppingCart) + .WithMany(f => f.ShoppingCartItems) + .HasForeignKey(f => f.ShoppingCartId) + .OnDelete(DeleteBehavior.NoAction); + } +} diff --git a/LiteCharms.Entities/Customer.cs b/LiteCharms.Entities/Customer.cs index d41be70..bd3bf40 100644 --- a/LiteCharms.Entities/Customer.cs +++ b/LiteCharms.Entities/Customer.cs @@ -8,4 +8,8 @@ public class Customer : Models.Customer public virtual ICollection? Leads { get; set; } public virtual ICollection? Orders { get; set; } + + public virtual ICollection? Quotes { get; set; } + + public virtual ICollection? ShoppingCarts { get; set; } } diff --git a/LiteCharms.Entities/Order.cs b/LiteCharms.Entities/Order.cs index 7d4d8c2..5f7d5dc 100644 --- a/LiteCharms.Entities/Order.cs +++ b/LiteCharms.Entities/Order.cs @@ -9,5 +9,7 @@ public class Order : Models.Order public virtual Customer? Customer { get; set; } - public virtual ProductPrice? ProductPrice { get; set; } + public virtual Quote? Quote { get; set; } + + public virtual ShoppingCart? ShoppingCart { get; set; } } diff --git a/LiteCharms.Entities/Quote.cs b/LiteCharms.Entities/Quote.cs new file mode 100644 index 0000000..8bacd89 --- /dev/null +++ b/LiteCharms.Entities/Quote.cs @@ -0,0 +1,13 @@ +using LiteCharms.Entities.Configuration; + +namespace LiteCharms.Entities; + +[EntityTypeConfiguration] +public class Quote : Models.Quote +{ + public virtual Customer? Customer { get; set; } + + public virtual ShoppingCart? ShoppingCart { get; set; } + + public virtual Order? Order { get; set; } +} diff --git a/LiteCharms.Entities/ShoppingCart.cs b/LiteCharms.Entities/ShoppingCart.cs new file mode 100644 index 0000000..3c974dc --- /dev/null +++ b/LiteCharms.Entities/ShoppingCart.cs @@ -0,0 +1,15 @@ +using LiteCharms.Entities.Configuration; + +namespace LiteCharms.Entities; + +[EntityTypeConfiguration] +public class ShoppingCart : Models.ShoppingCart +{ + public virtual Customer? Customer { get; set; } + + public virtual Order? Order { get; set; } + + public virtual Quote? Quote { get; set; } + + public virtual ICollection? ShoppingCartItems { get; set; } +} diff --git a/LiteCharms.Entities/ShoppingCartItem.cs b/LiteCharms.Entities/ShoppingCartItem.cs new file mode 100644 index 0000000..d5e5634 --- /dev/null +++ b/LiteCharms.Entities/ShoppingCartItem.cs @@ -0,0 +1,8 @@ +namespace LiteCharms.Entities; + +public class ShoppingCartItem : Models.ShoppingCartItem +{ + public virtual ShoppingCart? ShoppingCart { get; set; } + + public virtual ProductPrice? ProductPrice { get; set; } +} diff --git a/LiteCharms.Extensions/EntityModeMappers.cs b/LiteCharms.Extensions/EntityModeMappers.cs index 28230ba..8d41155 100644 --- a/LiteCharms.Extensions/EntityModeMappers.cs +++ b/LiteCharms.Extensions/EntityModeMappers.cs @@ -4,6 +4,41 @@ namespace LiteCharms.Extensions; public static class EntityModeMappers { + public static ShoppingCartItem ToModel(this Entities.ShoppingCartItem entity) => + new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + ProductPriceId = entity.ProductPriceId, + Quantity = entity.Quantity, + ShoppingCartId = entity.ShoppingCartId + }; + + public static ShoppingCart ToModel(this Entities.ShoppingCart entity) => + new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + CustomerId = entity.CustomerId, + OrderId = entity.OrderId, + QuoteId = entity.QuoteId + }; + + public static Quote ToModel(this Entities.Quote entity) => + new() + { + Id = entity.Id, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt, + CustomerId = entity.CustomerId, + ExpiredAt = entity.ExpiredAt, + Reason = entity.Reason, + ShoppingCartId = entity.ShoppingCartId, + Status = entity.Status + }; + public static Notification ToModel(this Entities.Notification entity) => new() { @@ -17,7 +52,8 @@ public static class EntityModeMappers Author = entity.Author, Platform = entity.Platform, PlatformAddress = entity.PlatformAddress, - Title = entity.Title + Title = entity.Title, + Processed = entity.Processed }; public static Customer ToModel(this Entities.Customer entity) => @@ -42,7 +78,7 @@ public static class EntityModeMappers Slack = entity.Slack, Tax = entity.Tax, Website = entity.Website, - Whatsapp = entity.Whatsapp + Whatsapp = entity.Whatsapp }; public static Lead ToModel(this Entities.Lead entity) => @@ -63,7 +99,7 @@ public static class EntityModeMappers ClickId = entity.ClickId, TargetId = entity.TargetId, WebClickId = entity.WebClickId, - Status = entity.Status + Status = entity.Status }; public static Order ToModel(this Entities.Order entity) => @@ -72,10 +108,12 @@ public static class EntityModeMappers Id = entity.Id, CreatedAt = entity.CreatedAt, UpdatedAt = entity.UpdatedAt, - CustomerId = entity.CustomerId, - ProductPriceId = entity.ProductPriceId, + CustomerId = entity.CustomerId, Notes = entity.Notes, - RefundId = entity.RefundId + RefundId = entity.RefundId, + QuoteId = entity.QuoteId, + Status = entity.Status, + ShoppingCartId = entity.ShoppingCartId }; public static OrderRefund ToModel(this Entities.OrderRefund entity) => @@ -106,6 +144,6 @@ public static class EntityModeMappers Active = entity.Active, CreatedAt = entity.CreatedAt, Discount = entity.Discount, - UpdatedAt = entity.UpdatedAt + UpdatedAt = entity.UpdatedAt }; } diff --git a/LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs b/LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs new file mode 100644 index 0000000..5ea7394 --- /dev/null +++ b/LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Customers.Queries; + +public class GetCustomerQuery : IRequest> +{ + public Guid CustomerId { get; set; } + + private GetCustomerQuery(Guid customerId) => CustomerId = customerId; + + public static GetCustomerQuery Create(Guid customerId) + { + if(customerId == Guid.Empty) + throw new ArgumentException("Customer ID is required.", nameof(customerId)); + + return new(customerId); + } +} diff --git a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs b/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs new file mode 100644 index 0000000..09f1f9b --- /dev/null +++ b/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs @@ -0,0 +1,26 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Customers.Queries.Handlers; + +public class GetCustomerQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetCustomerQuery request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == request.CustomerId, cancellationToken); + + return customer is not null + ? Result.Ok(customer.ToModel()) + : Result.Fail($"Customer not found with id {request.CustomerId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index 3eee68a..9534619 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -61,4 +61,8 @@ + + + + diff --git a/LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs b/LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs new file mode 100644 index 0000000..cfa40b1 --- /dev/null +++ b/LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs @@ -0,0 +1,63 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Notifications.Commands; + +public class CreateNotificationCommand : IRequest> +{ + public NotificationDirection Direction { get; set; } + + public string? Author { get; set; } + + public string? Title { get; set; } + + public string? Description { get; set; } + + public string? Platform { get; set; } + + public string? PlatformAddress { get; set; } + + public string? CorrelationId { get; set; } + + public string? CorrelationIdType { get; set; } + + public bool IsInternal { get; set; } + + private CreateNotificationCommand(NotificationDirection direction, string author, string title, string description, string platform, string platformAddress, string correlationId, string correlationIdType, bool isInternal) + { + Direction = direction; + Author = author; + Title = title; + Description = description; + Platform = platform; + PlatformAddress = platformAddress; + CorrelationId = correlationId; + CorrelationIdType = correlationIdType; + IsInternal = isInternal; + } + + public static CreateNotificationCommand Create(NotificationDirection direction, string author, string title, string description, string platform, string platformAddress, string correlationId, string correlationIdType, bool isInternal) + { + if (string.IsNullOrWhiteSpace(author)) + throw new ArgumentException("Author cannot be null or whitespace.", nameof(author)); + + if (string.IsNullOrWhiteSpace(title)) + throw new ArgumentException("Title cannot be null or whitespace.", nameof(title)); + + if (string.IsNullOrWhiteSpace(description)) + throw new ArgumentException("Description cannot be null or whitespace.", nameof(description)); + + if (string.IsNullOrWhiteSpace(platform)) + throw new ArgumentException("Platform cannot be null or whitespace.", nameof(platform)); + + if (string.IsNullOrWhiteSpace(platformAddress)) + throw new ArgumentException("PlatformAddress cannot be null or whitespace.", nameof(platformAddress)); + + if (string.IsNullOrWhiteSpace(correlationId)) + throw new ArgumentException("CorrelationId cannot be null or whitespace.", nameof(correlationId)); + + if (string.IsNullOrWhiteSpace(correlationIdType)) + throw new ArgumentException("CorrelationIdType cannot be null or whitespace.", nameof(correlationIdType)); + + return new(direction, author, title, description, platform, platformAddress, correlationId, correlationIdType, isInternal); + } +} diff --git a/LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs b/LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs new file mode 100644 index 0000000..e57569b --- /dev/null +++ b/LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs @@ -0,0 +1,35 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Notifications.Commands.Handlers; + +public class CreateNotificationCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(CreateNotificationCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var newNotification = context.Notifications.Add(new Entities.Notification + { + Direction = request.Direction, + Author = request.Author, + Title = request.Title, + Description = request.Description, + Platform = request.Platform, + PlatformAddress = request.PlatformAddress, + CorrelationId = request.CorrelationId, + CorrelationIdType = request.CorrelationIdType, + IsInternal = request.IsInternal, + }); + + return newNotification is not null + ? Result.Ok(newNotification.Entity.Id) + : Result.Fail(new Error("Failed to create notification")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs b/LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs new file mode 100644 index 0000000..2adb588 --- /dev/null +++ b/LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs @@ -0,0 +1,29 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Notifications.Commands.Handlers; + +public class UpdateNotificationCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +{ + public async ValueTask Handle(UpdateNotificationCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var notification = await context.Notifications.FirstOrDefaultAsync(n => n.Id == request.NotificationId, cancellationToken); + + if(notification is null) + return Result.Fail(new Error($"Notification with id {request.NotificationId} not found.")); + + notification.Processed = request.Processed; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to update notification with id {request.NotificationId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs b/LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs new file mode 100644 index 0000000..d5961f2 --- /dev/null +++ b/LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs @@ -0,0 +1,22 @@ +namespace LiteCharms.Features.Notifications.Commands; + +public class UpdateNotificationCommand : IRequest +{ + public Guid NotificationId { get; set; } + + public bool Processed { get; set; } + + private UpdateNotificationCommand(Guid notificationId, bool processed) + { + NotificationId = notificationId; + Processed = processed; + } + + public static UpdateNotificationCommand Create(Guid notificationId, bool processed) + { + if(notificationId == Guid.Empty) + throw new ArgumentException("Notification ID cannot be empty.", nameof(notificationId)); + + return new(notificationId, processed); + } +} diff --git a/LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs b/LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs new file mode 100644 index 0000000..f41aea5 --- /dev/null +++ b/LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Notifications.Queries; + +public class GetNotificationQuery : IRequest> +{ + public Guid NotificationId { get; set; } + + private GetNotificationQuery(Guid notificationId) => NotificationId = notificationId; + + public static GetNotificationQuery Create(Guid notificationId) + { + if (notificationId == Guid.Empty) + throw new ArgumentException("Notification ID is required.", nameof(notificationId)); + + return new(notificationId); + } +} diff --git a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs b/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs new file mode 100644 index 0000000..133e3bf --- /dev/null +++ b/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs @@ -0,0 +1,26 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Notifications.Queries.Handlers; + +public class GetNotificationQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetNotificationQuery request, CancellationToken cancellationToken) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var notification = await context.Notifications.FindAsync(new object[] { request.NotificationId }, cancellationToken); + + return notification is not null + ? Result.Ok(notification.ToModel()) + : Result.Fail(new Error($"Notification with id {request.NotificationId} not found")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs b/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs index 2d1a6a2..f6ac872 100644 --- a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs +++ b/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs @@ -15,7 +15,7 @@ public class GetNotificationsQueryHandler(IDbContextFactory n.CreatedAt >= fromDate && n.CreatedAt <= toDate) .OrderByDescending(n => n.CreatedAt) .Take(request.MaxRecords) diff --git a/LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs b/LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs index 39a7a27..e18258e 100644 --- a/LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs +++ b/LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs @@ -4,22 +4,25 @@ public class CreateOrderCommand : IRequest> { public Guid CustomerId { get; set; } - public Guid ProductPriceId { get; set; } + public Guid ShoppingCartId { get; set; } - private CreateOrderCommand(Guid customerId, Guid productPriceId) + public Guid? QuoteId { get; set; } + + private CreateOrderCommand(Guid customerId, Guid shoppingCartId, Guid? quoteId = null) { CustomerId = customerId; - ProductPriceId = productPriceId; + ShoppingCartId = shoppingCartId; + QuoteId = quoteId; } - public static CreateOrderCommand Create(Guid customerId, Guid productPriceId) + public static CreateOrderCommand Create(Guid customerId, Guid shoppingCartId, Guid? quoteId = null) { if (customerId == Guid.Empty) throw new ArgumentException("CustomerId is required.", nameof(customerId)); - if (productPriceId == Guid.Empty) - throw new ArgumentException("ProductPriceId is required.", nameof(productPriceId)); + if (shoppingCartId == Guid.Empty) + throw new ArgumentException("ShoppingCartId is required.", nameof(shoppingCartId)); - return new(customerId, productPriceId); + return new(customerId, shoppingCartId, quoteId); } } diff --git a/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs b/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs index f744f89..1200afb 100644 --- a/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs +++ b/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs @@ -10,16 +10,26 @@ public class CreateOrderCommandHandler(IDbContextFactory { using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + if(!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken)) + return Result.Fail(new Error($"Customer {request.CustomerId} does not exist.")); + + if(!await context.ShoppingCarts.AnyAsync(sc => sc.Id == request.ShoppingCartId, cancellationToken)) + return Result.Fail(new Error($"Shopping cart {request.ShoppingCartId} does not exist.")); + + if(request.QuoteId.HasValue && !await context.Quotes.AnyAsync(q => q.Id == request.QuoteId.Value, cancellationToken)) + return Result.Fail(new Error($"Quote {request.QuoteId.Value} does not exist.")); + var newOrder = context.Orders.Add(new Entities.Order { CustomerId = request.CustomerId, - ProductPriceId = request.ProductPriceId, + ShoppingCartId = request.ShoppingCartId, + QuoteId = request.QuoteId, CreatedAt = DateTime.UtcNow }); return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(newOrder.Entity.Id) - : Result.Fail(new Error($"Failed to create customer {request.CustomerId} order using product price {request.ProductPriceId}.")); + : Result.Fail(new Error($"Failed to create customer {request.CustomerId} order using shopping cart {request.ShoppingCartId}.")); } catch (Exception ex) { diff --git a/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderCommandHandler.cs b/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs similarity index 75% rename from LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderCommandHandler.cs rename to LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs index f9bf460..2524b79 100644 --- a/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderCommandHandler.cs +++ b/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs @@ -2,9 +2,9 @@ namespace LiteCharms.Features.Orders.Commands.Handlers; -public class UpdateOrderCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +public class UpdateOrderStatusCommandHandler(IDbContextFactory contextFactory) : IRequestHandler { - public async ValueTask Handle(UpdateOrderCommand request, CancellationToken cancellationToken) + public async ValueTask Handle(UpdateOrderStatusCommand request, CancellationToken cancellationToken) { try { diff --git a/LiteCharms.Features/Orders/Commands/UpdateOrderCommand.cs b/LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs similarity index 71% rename from LiteCharms.Features/Orders/Commands/UpdateOrderCommand.cs rename to LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs index ee1b844..3e6d1c6 100644 --- a/LiteCharms.Features/Orders/Commands/UpdateOrderCommand.cs +++ b/LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs @@ -2,7 +2,7 @@ namespace LiteCharms.Features.Orders.Commands; -public class UpdateOrderCommand : IRequest +public class UpdateOrderStatusCommand : IRequest { public Guid OrderId { get; set; } @@ -10,14 +10,14 @@ public class UpdateOrderCommand : IRequest public string? Note { get; set; } - private UpdateOrderCommand(Guid orderId, OrderStatus status, string? note) + private UpdateOrderStatusCommand(Guid orderId, OrderStatus status, string? note) { OrderId = orderId; Status = status; Note = note; } - public static UpdateOrderCommand Create(Guid orderId, OrderStatus status, string? note) + public static UpdateOrderStatusCommand Create(Guid orderId, OrderStatus status, string? note) { if (orderId == Guid.Empty) throw new ArgumentException("OrderId is required.", nameof(orderId)); diff --git a/LiteCharms.Features/Customers/Queries/GetCustomerOrdersQuery.cs b/LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs similarity index 90% rename from LiteCharms.Features/Customers/Queries/GetCustomerOrdersQuery.cs rename to LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs index 26b7c0e..7d11f91 100644 --- a/LiteCharms.Features/Customers/Queries/GetCustomerOrdersQuery.cs +++ b/LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs @@ -1,6 +1,6 @@ using LiteCharms.Models; -namespace LiteCharms.Features.Customers.Queries; +namespace LiteCharms.Features.Orders.Queries; public class GetCustomerOrdersQuery : IRequest> { diff --git a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerOrdersQueryHandler.cs b/LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs similarity index 78% rename from LiteCharms.Features/Customers/Queries/Handlers/GetCustomerOrdersQueryHandler.cs rename to LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs index 66f1140..6d683ce 100644 --- a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerOrdersQueryHandler.cs +++ b/LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs @@ -2,7 +2,7 @@ using LiteCharms.Infrastructure.Database; using LiteCharms.Models; -namespace LiteCharms.Features.Customers.Queries.Handlers; +namespace LiteCharms.Features.Orders.Queries.Handlers; public class GetCustomerOrdersQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> { @@ -12,6 +12,9 @@ public class GetCustomerOrdersQueryHandler(IDbContextFactory c.Id == request.CustomerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id {request.CustomerId} does not exist.")); + var orders = await context.Orders.AsNoTracking() .OrderByDescending(o => o.CreatedAt) .Where(o => o.CustomerId == request.CustomerId) diff --git a/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs b/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs index 28ba212..12bcc9f 100644 --- a/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs +++ b/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs @@ -12,6 +12,9 @@ public class GetProductPriceQueryHandler(IDbContextFactory p.Id == request.ProductId, cancellationToken)) + return Result.Fail(new Error($"Product {request.ProductId} not found.")); + var productPrice = await context.ProductPrices.AsNoTracking() .Where(pp => pp.ProductId == request.ProductId && pp.Active) .OrderByDescending(pp => pp.CreatedAt) diff --git a/LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs b/LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs new file mode 100644 index 0000000..498aa4a --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs @@ -0,0 +1,25 @@ +namespace LiteCharms.Features.Quotes.Commands; + +public class AssignQuoteToOrderCommand : IRequest +{ + public Guid OrderId { get; set; } + + public Guid QuoteId { get; set; } + + private AssignQuoteToOrderCommand(Guid orderId, Guid quoteId) + { + OrderId = orderId; + QuoteId = quoteId; + } + + public static AssignQuoteToOrderCommand Create(Guid orderId, Guid quoteId) + { + if(orderId == Guid.Empty) + throw new ArgumentException("Order ID is required.", nameof(orderId)); + + if(quoteId == Guid.Empty) + throw new ArgumentException("Quote ID is required.", nameof(quoteId)); + + return new AssignQuoteToOrderCommand(orderId, quoteId); + } +} diff --git a/LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs b/LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs new file mode 100644 index 0000000..61c0902 --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs @@ -0,0 +1,25 @@ +namespace LiteCharms.Features.Quotes.Commands; + +public class AssignQuoteToShoppingCartCommand : IRequest +{ + public Guid QuoteId { get; set; } + + public Guid ShoppingCartId { get; set; } + + private AssignQuoteToShoppingCartCommand(Guid quoteId, Guid shoppingCartId) + { + QuoteId = quoteId; + ShoppingCartId = shoppingCartId; + } + + public static AssignQuoteToShoppingCartCommand Create(Guid quoteId, Guid shoppingCartId) + { + if(quoteId == Guid.Empty) + throw new ArgumentException("QuoteId cannot be empty.", nameof(quoteId)); + + if (shoppingCartId == Guid.Empty) + throw new ArgumentException("ShoppingCartId cannot be empty.", nameof(shoppingCartId)); + + return new(quoteId, shoppingCartId); + } +} diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs b/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs new file mode 100644 index 0000000..4178e6b --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs @@ -0,0 +1,35 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Quotes.Commands.Handlers; + +public class AssignQuoteToOrderCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +{ + public async ValueTask Handle(AssignQuoteToOrderCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var order = await context.Orders.FirstOrDefaultAsync(o => o.Id == request.OrderId, cancellationToken); + + if (order is null) + return Result.Fail(new Error($"Order with id {request.OrderId} not found")); + + if(!await context.Quotes.AnyAsync(q => q.Id == request.OrderId, cancellationToken)) + return Result.Fail(new Error($"Quote with id {request.QuoteId} not found")); + + if(order.QuoteId == request.QuoteId) + return Result.Fail(new Error($"Quote with id {request.QuoteId} is already assigned to order with id {request.OrderId}")); + + order.QuoteId = request.QuoteId; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to assign quote with id {request.QuoteId} to order with id {request.OrderId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs b/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs new file mode 100644 index 0000000..8638079 --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs @@ -0,0 +1,32 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Quotes.Commands.Handlers; + +public class AssignQuoteToShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +{ + public async ValueTask Handle(AssignQuoteToShoppingCartCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var shoppingCart = await context.Orders.FirstOrDefaultAsync(o => o.Id == request.ShoppingCartId, cancellationToken); + + if (shoppingCart is null) + return Result.Fail(new Error($"ShoppingCart with id {request.ShoppingCartId} not found")); + + if(!await context.Quotes.AnyAsync(q => q.Id == request.QuoteId, cancellationToken)) + return Result.Fail(new Error($"Quote with id {request.QuoteId} not found")); + + shoppingCart.QuoteId = request.QuoteId; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to assign quote to shopping cart")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs b/LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs new file mode 100644 index 0000000..70c5191 --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs @@ -0,0 +1,29 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Quotes.Commands.Handlers; + +public class UpdateQuoteStatusCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +{ + public async ValueTask Handle(UpdateQuoteStatusCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.FirstOrDefaultAsync(q => q.Id == request.QuoteId, cancellationToken); + + if (quote is null) + return Result.Fail(new Error("Quote not found.")); + + quote.Status = request.Status; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error("Failed to update quote status.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs b/LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs new file mode 100644 index 0000000..901bc46 --- /dev/null +++ b/LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs @@ -0,0 +1,24 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Commands; + +public class UpdateQuoteStatusCommand : IRequest +{ + public Guid QuoteId { get; set; } + + public QuoteStatus Status { get; set; } + + private UpdateQuoteStatusCommand(Guid quoteId, QuoteStatus status) + { + QuoteId = quoteId; + Status = status; + } + + public static UpdateQuoteStatusCommand Create(Guid quoteId, QuoteStatus status) + { + if(quoteId == Guid.Empty) + throw new ArgumentException("Quote ID cannot be empty.", nameof(quoteId)); + + return new(quoteId, status); + } +} diff --git a/LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs b/LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs new file mode 100644 index 0000000..d531b3a --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries; + +public class GetCustomerQuotesQuery : IRequest> +{ + public Guid CustomerId { get; set; } + + private GetCustomerQuotesQuery(Guid customerId) => CustomerId = customerId; + + public static GetCustomerQuotesQuery Create(Guid customerId) + { + if (customerId == Guid.Empty) + throw new ArgumentException("CustomerId is required."); + + return new(customerId); + } +} \ No newline at end of file diff --git a/LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs b/LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs new file mode 100644 index 0000000..0f566cf --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries; + +public class GetQuoteQuery : IRequest> +{ + public Guid QuoteId { get; set; } + + private GetQuoteQuery(Guid quoteId) => QuoteId = quoteId; + + public static GetQuoteQuery Create(Guid quoteId) + { + if(quoteId == Guid.Empty) + throw new ArgumentException("Quote ID is required.", nameof(quoteId)); + + return new(quoteId); + } +} diff --git a/LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs b/LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs new file mode 100644 index 0000000..20a8d4b --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs @@ -0,0 +1,30 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries; + +public class GetQuotesQuery : IRequest> +{ + public DateOnly From { get; set; } + + public DateOnly To { get; set; } + + public int MaxRecords { get; set; } + + private GetQuotesQuery(DateOnly from, DateOnly to, int maxRecords = 1000) + { + From = from; + To = to; + MaxRecords = maxRecords; + } + + public static GetQuotesQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000) + { + if (from > to) + throw new ArgumentException("From date cannot be greater than To date."); + + if (maxRecords <= 0) + throw new ArgumentException("MaxRecords must be a positive integer."); + + return new(from, to, maxRecords); + } +} diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs b/LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs new file mode 100644 index 0000000..3243924 --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs @@ -0,0 +1,31 @@ +using LiteCharms.Extensions; +using LiteCharms.Features.Quotes.Queries; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries.Handlers; + +public class GetCustomerQuotesQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetCustomerQuotesQuery 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(new Error($"Customer with Id {request.CustomerId} does not exist.")); + + var quotes = await context.Quotes.AsNoTracking() + .Where(q => q.CustomerId == request.CustomerId).ToArrayAsync(cancellationToken); + + return quotes?.Length > 0 + ? Result.Ok(quotes.Select(q => q.ToModel()).ToArray()) + : Result.Fail(new Error($"No quotes found for customer with Id {request.CustomerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs b/LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs new file mode 100644 index 0000000..d8343cd --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs @@ -0,0 +1,26 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries.Handlers; + +public class GetQuoteQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetQuoteQuery request, CancellationToken cancellationToken) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.AsNoTracking().FirstOrDefaultAsync(q => q.Id == request.QuoteId, cancellationToken); + + return quote is not null + ? Result.Ok(quote.ToModel()) + : Result.Fail(new Error($"Quote with ID {request.QuoteId} not found.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs b/LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs new file mode 100644 index 0000000..adfae5e --- /dev/null +++ b/LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs @@ -0,0 +1,33 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Quotes.Queries.Handlers; + +public class GetQuotesHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetQuotesQuery request, CancellationToken cancellationToken) + { + try + { + var fromDate = request.From.ToDateTime(TimeOnly.MinValue); + var toDate = request.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quotes = await context.Quotes.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate) + .Take(request.MaxRecords) + .ToArrayAsync(cancellationToken); + + return quotes?.Length > 0 + ? Result.Ok(quotes.Select(o => o.ToModel()).ToArray()) + : Result.Fail(new Error($"No quotes found for the specified date range {request.From} - {request.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Customers/Commands/Handlers/RefundCustomerCommandHandler.cs b/LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs similarity index 96% rename from LiteCharms.Features/Customers/Commands/Handlers/RefundCustomerCommandHandler.cs rename to LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs index 1d3af5e..0b52018 100644 --- a/LiteCharms.Features/Customers/Commands/Handlers/RefundCustomerCommandHandler.cs +++ b/LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Infrastructure.Database; -namespace LiteCharms.Features.Customers.Commands.Handlers; +namespace LiteCharms.Features.Refunds.Commands.Handlers; public class RefundCustomerCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> { diff --git a/LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs b/LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs new file mode 100644 index 0000000..de1bf04 --- /dev/null +++ b/LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs @@ -0,0 +1,30 @@ +using LiteCharms.Infrastructure.Database; + +namespace LiteCharms.Features.Refunds.Commands.Handlers; + +public class UpdateOrderRefundCommandHandler(IDbContextFactory contextFactory) : IRequestHandler +{ + public async ValueTask Handle(UpdateOrderRefundCommand request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refund = await context.OrderRefunds.FirstOrDefaultAsync(r => r.Id == request.OrderRefundId, cancellationToken); + + if (refund is null) + return Result.Fail($"Order refund not found with id {request.OrderRefundId}"); + + refund.Reason = request.Reason; + refund.Amount = request.Amount; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to update order refund {request.OrderRefundId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Customers/Commands/RefundCustomerCommand.cs b/LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs similarity index 95% rename from LiteCharms.Features/Customers/Commands/RefundCustomerCommand.cs rename to LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs index 6b22a2a..ccfda6e 100644 --- a/LiteCharms.Features/Customers/Commands/RefundCustomerCommand.cs +++ b/LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Features.Customers.Commands; +namespace LiteCharms.Features.Refunds.Commands; public class RefundCustomerCommand : IRequest> { diff --git a/LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs b/LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs new file mode 100644 index 0000000..bd9ab59 --- /dev/null +++ b/LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs @@ -0,0 +1,28 @@ +namespace LiteCharms.Features.Refunds.Commands; + +public class UpdateOrderRefundCommand : IRequest +{ + public Guid OrderRefundId { get; set; } + + public string? Reason { get; set; } + + public decimal Amount { get; set; } + + private UpdateOrderRefundCommand(Guid orderRefundId, string? reason, decimal amount) + { + OrderRefundId = orderRefundId; + Reason = reason; + Amount = amount; + } + + public static UpdateOrderRefundCommand Create(Guid orderRefundId, string? reason, decimal amount) + { + if (orderRefundId == Guid.Empty) + throw new ArgumentException("Order refund id is required.", nameof(orderRefundId)); + + if (string.IsNullOrWhiteSpace(reason)) + throw new ArgumentException("Refund update reason is required"); + + return new(orderRefundId, reason, amount); + } +} diff --git a/LiteCharms.Features/Customers/Queries/GetCustomerRefundsQuery.cs b/LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs similarity index 90% rename from LiteCharms.Features/Customers/Queries/GetCustomerRefundsQuery.cs rename to LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs index eb4b7a2..24d2cc9 100644 --- a/LiteCharms.Features/Customers/Queries/GetCustomerRefundsQuery.cs +++ b/LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs @@ -1,6 +1,6 @@ using LiteCharms.Models; -namespace LiteCharms.Features.Customers.Queries; +namespace LiteCharms.Features.Refunds.Queries; public class GetCustomerRefundsQuery : IRequest> { diff --git a/LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs b/LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs new file mode 100644 index 0000000..9f4d375 --- /dev/null +++ b/LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.Refunds.Queries; + +public class GetRefundQuery : IRequest> +{ + public Guid OrderRefundId { get; set; } + + private GetRefundQuery(Guid orderRefundId) => OrderRefundId = orderRefundId; + + public static GetRefundQuery Create(Guid orderRefundId) + { + if(orderRefundId == Guid.Empty) + throw new ArgumentException("Customer ID is required.", nameof(orderRefundId)); + + return new(orderRefundId); + } +} diff --git a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerRefundsQueryHandler.cs b/LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Customers/Queries/Handlers/GetCustomerRefundsQueryHandler.cs rename to LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs index 26188af..34b9f21 100644 --- a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerRefundsQueryHandler.cs +++ b/LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs @@ -1,8 +1,9 @@ using LiteCharms.Extensions; +using LiteCharms.Features.Refunds.Queries; using LiteCharms.Infrastructure.Database; using LiteCharms.Models; -namespace LiteCharms.Features.Customers.Queries.Handlers; +namespace LiteCharms.Features.Refunds.Queries.Handlers; public class GetCustomerRefundsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> { diff --git a/LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs b/LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs new file mode 100644 index 0000000..29bd3e5 --- /dev/null +++ b/LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs @@ -0,0 +1,26 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.Refunds.Queries.Handlers; + +public class GetRefundQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetRefundQuery request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refund = await context.OrderRefunds.AsNoTracking().FirstOrDefaultAsync(r => r.Id == request.OrderRefundId, cancellationToken); + + return refund is not null + ? Result.Ok(refund.ToModel()) + : Result.Fail($"Order refund could not be found with id {request.OrderRefundId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs b/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs new file mode 100644 index 0000000..3c5098e --- /dev/null +++ b/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs @@ -0,0 +1,6 @@ +namespace LiteCharms.Features.ShoppingCarts.Commands; + +public class CreateShoppingCartCommand : IRequest +{ + +} diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs b/LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs new file mode 100644 index 0000000..0d64287 --- /dev/null +++ b/LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.ShoppingCarts.Queries; + +public class GetCustomerShoppingCartsQuery : IRequest> +{ + public Guid CustomerId { get; set; } + + private GetCustomerShoppingCartsQuery(Guid customerId) => CustomerId = customerId; + + public static GetCustomerShoppingCartsQuery Create(Guid customerId) + { + if(customerId == Guid.Empty) + throw new ArgumentException("Customer ID is required.", nameof(customerId)); + + return new(customerId); + } +} diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs b/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs new file mode 100644 index 0000000..07dfc5a --- /dev/null +++ b/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs @@ -0,0 +1,18 @@ +using LiteCharms.Models; + +namespace LiteCharms.Features.ShoppingCarts.Queries; + +public class GetShoppingCartQuery : IRequest> +{ + public Guid ShoppingCartId { get; set; } + + private GetShoppingCartQuery(Guid shoppingCartId) => ShoppingCartId = shoppingCartId; + + public static GetShoppingCartQuery 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/GetCustomerShoppingCartsQueryHandler.cs b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs new file mode 100644 index 0000000..d4a6885 --- /dev/null +++ b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs @@ -0,0 +1,30 @@ +using LiteCharms.Extensions; +using LiteCharms.Features.ShoppingCarts.Queries; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; + +public class GetCustomerShoppingCartsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetCustomerShoppingCartsQuery 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(new Error($"Customer with Id {request.CustomerId} does not exist.")); + + var shoppingCarts = await context.ShoppingCarts.Where(sc => sc.CustomerId == request.CustomerId).ToArrayAsync(cancellationToken); + + return shoppingCarts?.Length > 0 + ? Result.Ok(shoppingCarts.Select(c => c.ToModel()).ToArray()) + : Result.Fail(new Error($"No shopping carts found for customer with Id {request.CustomerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs new file mode 100644 index 0000000..d58ffc3 --- /dev/null +++ b/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs @@ -0,0 +1,26 @@ +using LiteCharms.Extensions; +using LiteCharms.Infrastructure.Database; +using LiteCharms.Models; + +namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; + +public class GetShoppingCartQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> +{ + public async ValueTask> Handle(GetShoppingCartQuery request, CancellationToken cancellationToken) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var cart = await context.ShoppingCarts.AsNoTracking().FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken); + + return cart is not null + ? Result.Ok(cart.ToModel()) + : Result.Fail($"Failed to find shopping cart with id {request.ShoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Infrastructure/Database/LeadGeneratorDbContext.cs b/LiteCharms.Infrastructure/Database/LeadGeneratorDbContext.cs index 59447d8..b3e7195 100644 --- a/LiteCharms.Infrastructure/Database/LeadGeneratorDbContext.cs +++ b/LiteCharms.Infrastructure/Database/LeadGeneratorDbContext.cs @@ -17,4 +17,10 @@ public class LeadGeneratorDbContext(DbContextOptions opt public DbSet ProductPrices { get; set; } public DbSet Notifications { get; set; } + + public DbSet Quotes { get; set; } + + public DbSet ShoppingCarts { get; set; } + + public DbSet ShoppingCartItems { get; set; } } diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.Designer.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.Designer.cs new file mode 100644 index 0000000..afd8292 --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.Designer.cs @@ -0,0 +1,409 @@ +// +using System; +using LiteCharms.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + [DbContext(typeof(LeadGeneratorDbContext))] + [Migration("20260505123745_AddedNotifications")] + partial class AddedNotifications + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discord") + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LinkedIn") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("Region") + .HasColumnType("text"); + + b.Property("Slack") + .HasColumnType("text"); + + b.Property("Tax") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customer", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdGroupId") + .HasColumnType("bigint"); + + b.Property("AdName") + .HasColumnType("bigint"); + + b.Property("AppClickId") + .HasColumnType("text"); + + b.Property("AttributionHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("CampaignId") + .HasColumnType("bigint"); + + b.Property("ClickId") + .HasColumnType("text"); + + b.Property("ClickLocation") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("FeedItemId") + .HasColumnType("bigint"); + + b.Property("Source") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TargetId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("WebClickId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Lead", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationIdType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsInternal") + .HasColumnType("boolean"); + + b.Property("Platform") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlatformAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.Property("RefundId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("Order", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.ToTable("OrderRefund", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Product", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductPrice", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Leads") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("Refund") + .HasForeignKey("LiteCharms.Entities.OrderRefund", "OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Entities.Product", "Product") + .WithMany("ProductPrices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Navigation("Leads"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Navigation("Refund"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Navigation("ProductPrices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.cs new file mode 100644 index 0000000..c39cf39 --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505123745_AddedNotifications.cs @@ -0,0 +1,43 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + /// + public partial class AddedNotifications : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Notification", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Direction = table.Column(type: "integer", nullable: false), + Author = table.Column(type: "text", nullable: false), + Title = table.Column(type: "text", nullable: false), + Description = table.Column(type: "text", nullable: false), + Platform = table.Column(type: "text", nullable: false), + PlatformAddress = table.Column(type: "text", nullable: false), + CorrelationId = table.Column(type: "text", nullable: false), + CorrelationIdType = table.Column(type: "text", nullable: false), + IsInternal = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Notification", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Notification"); + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.Designer.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.Designer.cs new file mode 100644 index 0000000..ae0228c --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.Designer.cs @@ -0,0 +1,416 @@ +// +using System; +using LiteCharms.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + [DbContext(typeof(LeadGeneratorDbContext))] + [Migration("20260505124135_AddedProcessedColumnToNotifications")] + partial class AddedProcessedColumnToNotifications + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discord") + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LinkedIn") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("Region") + .HasColumnType("text"); + + b.Property("Slack") + .HasColumnType("text"); + + b.Property("Tax") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customer", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdGroupId") + .HasColumnType("bigint"); + + b.Property("AdName") + .HasColumnType("bigint"); + + b.Property("AppClickId") + .HasColumnType("text"); + + b.Property("AttributionHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("CampaignId") + .HasColumnType("bigint"); + + b.Property("ClickId") + .HasColumnType("text"); + + b.Property("ClickLocation") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("FeedItemId") + .HasColumnType("bigint"); + + b.Property("Source") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TargetId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("WebClickId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Lead", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationIdType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsInternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Platform") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlatformAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("Processed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.Property("RefundId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("Order", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.ToTable("OrderRefund", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Product", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductPrice", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Leads") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("Refund") + .HasForeignKey("LiteCharms.Entities.OrderRefund", "OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Entities.Product", "Product") + .WithMany("ProductPrices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Navigation("Leads"); + + b.Navigation("Orders"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Navigation("Refund"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Navigation("ProductPrices"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.cs new file mode 100644 index 0000000..3439cab --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505124135_AddedProcessedColumnToNotifications.cs @@ -0,0 +1,47 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + /// + public partial class AddedProcessedColumnToNotifications : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AlterColumn( + name: "IsInternal", + table: "Notification", + type: "boolean", + nullable: false, + defaultValue: true, + oldClrType: typeof(bool), + oldType: "boolean"); + + migrationBuilder.AddColumn( + name: "Processed", + table: "Notification", + type: "boolean", + nullable: false, + defaultValue: false); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Processed", + table: "Notification"); + + migrationBuilder.AlterColumn( + name: "IsInternal", + table: "Notification", + type: "boolean", + nullable: false, + oldClrType: typeof(bool), + oldType: "boolean", + oldDefaultValue: true); + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.Designer.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.Designer.cs new file mode 100644 index 0000000..3707216 --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.Designer.cs @@ -0,0 +1,604 @@ +// +using System; +using LiteCharms.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + [DbContext(typeof(LeadGeneratorDbContext))] + [Migration("20260505202859_AddedQuoteShoppingCartalteredOrderCustomer")] + partial class AddedQuoteShoppingCartalteredOrderCustomer + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discord") + .HasColumnType("text"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("text"); + + b.Property("LinkedIn") + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Phone") + .HasColumnType("text"); + + b.Property("PostalCode") + .HasColumnType("text"); + + b.Property("Region") + .HasColumnType("text"); + + b.Property("Slack") + .HasColumnType("text"); + + b.Property("Tax") + .HasColumnType("text"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customer", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AdGroupId") + .HasColumnType("bigint"); + + b.Property("AdName") + .HasColumnType("bigint"); + + b.Property("AppClickId") + .HasColumnType("text"); + + b.Property("AttributionHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("CampaignId") + .HasColumnType("bigint"); + + b.Property("ClickId") + .HasColumnType("text"); + + b.Property("ClickLocation") + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("FeedItemId") + .HasColumnType("bigint"); + + b.Property("Source") + .HasColumnType("text"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("TargetId") + .HasColumnType("bigint"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.Property("WebClickId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Lead", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationIdType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsInternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Platform") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlatformAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("Processed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.Property("QuoteId") + .HasColumnType("uuid"); + + b.Property("RefundId") + .HasColumnType("uuid"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("QuoteId") + .IsUnique(); + + b.HasIndex("ShoppingCartId") + .IsUnique(); + + b.ToTable("Order", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Amount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.ToTable("OrderRefund", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Product", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Discount") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("Price") + .HasPrecision(18, 2) + .HasColumnType("numeric(18,2)"); + + b.Property("ProductId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductPrice", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("CustomerId1") + .HasColumnType("uuid"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("CustomerId1"); + + b.HasIndex("ShoppingCartId") + .IsUnique(); + + b.ToTable("Quote", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("QuoteId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("ShoppingCart", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductPriceId"); + + b.HasIndex("ShoppingCartId"); + + b.ToTable("ShoppingCartItems"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Lead", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Leads") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.Quote", "Quote") + .WithOne("Order") + .HasForeignKey("LiteCharms.Entities.Order", "QuoteId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithOne("Order") + .HasForeignKey("LiteCharms.Entities.Order", "ShoppingCartId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("Quote"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + { + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("Refund") + .HasForeignKey("LiteCharms.Entities.OrderRefund", "OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Entities.Product", "Product") + .WithMany("ProductPrices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.Customer", null) + .WithMany("Quotes") + .HasForeignKey("CustomerId1"); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("ShoppingCarts") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + { + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithMany("ShoppingCartItems") + .HasForeignKey("ShoppingCartId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProductPrice"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Customer", b => + { + b.Navigation("Leads"); + + b.Navigation("Orders"); + + b.Navigation("Quotes"); + + b.Navigation("ShoppingCarts"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Order", b => + { + b.Navigation("Refund"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Product", b => + { + b.Navigation("ProductPrices"); + }); + + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.Navigation("Order"); + + b.Navigation("Quote"); + + b.Navigation("ShoppingCartItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.cs b/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.cs new file mode 100644 index 0000000..8442535 --- /dev/null +++ b/LiteCharms.Infrastructure/Database/Migrations/20260505202859_AddedQuoteShoppingCartalteredOrderCustomer.cs @@ -0,0 +1,227 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LiteCharms.Infrastructure.Database.Migrations +{ + /// + public partial class AddedQuoteShoppingCartalteredOrderCustomer : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Order_ProductPrice_ProductPriceId", + table: "Order"); + + migrationBuilder.DropIndex( + name: "IX_Order_ProductPriceId", + table: "Order"); + + migrationBuilder.RenameColumn( + name: "ProductPriceId", + table: "Order", + newName: "ShoppingCartId"); + + migrationBuilder.AddColumn( + name: "QuoteId", + table: "Order", + type: "uuid", + nullable: true); + + migrationBuilder.CreateTable( + name: "ShoppingCart", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: true), + OrderId = table.Column(type: "uuid", nullable: true), + QuoteId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ShoppingCart", x => x.Id); + table.ForeignKey( + name: "FK_ShoppingCart_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Quote", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CustomerId1 = table.Column(type: "uuid", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + ExpiredAt = table.Column(type: "timestamp with time zone", nullable: true), + CustomerId = table.Column(type: "uuid", nullable: false), + ShoppingCartId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Reason = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Quote", x => x.Id); + table.ForeignKey( + name: "FK_Quote_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Quote_Customer_CustomerId1", + column: x => x.CustomerId1, + principalTable: "Customer", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Quote_ShoppingCart_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCart", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCartItems", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ShoppingCartId = table.Column(type: "uuid", nullable: false), + ProductPriceId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false), + Quantity = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShoppingCartItems", x => x.Id); + table.ForeignKey( + name: "FK_ShoppingCartItems_ProductPrice_ProductPriceId", + column: x => x.ProductPriceId, + principalTable: "ProductPrice", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ShoppingCartItems_ShoppingCart_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCart", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Order_QuoteId", + table: "Order", + column: "QuoteId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Order_ShoppingCartId", + table: "Order", + column: "ShoppingCartId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Quote_CustomerId", + table: "Quote", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Quote_CustomerId1", + table: "Quote", + column: "CustomerId1"); + + migrationBuilder.CreateIndex( + name: "IX_Quote_ShoppingCartId", + table: "Quote", + column: "ShoppingCartId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCart_CustomerId", + table: "ShoppingCart", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartItems_ProductPriceId", + table: "ShoppingCartItems", + column: "ProductPriceId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartItems_ShoppingCartId", + table: "ShoppingCartItems", + column: "ShoppingCartId"); + + migrationBuilder.AddForeignKey( + name: "FK_Order_Quote_QuoteId", + table: "Order", + column: "QuoteId", + principalTable: "Quote", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_Order_ShoppingCart_ShoppingCartId", + table: "Order", + column: "ShoppingCartId", + principalTable: "ShoppingCart", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Order_Quote_QuoteId", + table: "Order"); + + migrationBuilder.DropForeignKey( + name: "FK_Order_ShoppingCart_ShoppingCartId", + table: "Order"); + + migrationBuilder.DropTable( + name: "Quote"); + + migrationBuilder.DropTable( + name: "ShoppingCartItems"); + + migrationBuilder.DropTable( + name: "ShoppingCart"); + + migrationBuilder.DropIndex( + name: "IX_Order_QuoteId", + table: "Order"); + + migrationBuilder.DropIndex( + name: "IX_Order_ShoppingCartId", + table: "Order"); + + migrationBuilder.DropColumn( + name: "QuoteId", + table: "Order"); + + migrationBuilder.RenameColumn( + name: "ShoppingCartId", + table: "Order", + newName: "ProductPriceId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_ProductPriceId", + table: "Order", + column: "ProductPriceId"); + + migrationBuilder.AddForeignKey( + name: "FK_Order_ProductPrice_ProductPriceId", + table: "Order", + column: "ProductPriceId", + principalTable: "ProductPrice", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + } + } +} diff --git a/LiteCharms.Infrastructure/Database/Migrations/LeadGeneratorDbContextModelSnapshot.cs b/LiteCharms.Infrastructure/Database/Migrations/LeadGeneratorDbContextModelSnapshot.cs index e2ad938..a057ff3 100644 --- a/LiteCharms.Infrastructure/Database/Migrations/LeadGeneratorDbContextModelSnapshot.cs +++ b/LiteCharms.Infrastructure/Database/Migrations/LeadGeneratorDbContextModelSnapshot.cs @@ -158,6 +158,62 @@ namespace LiteCharms.Infrastructure.Migrations b.ToTable("Lead", (string)null); }); + modelBuilder.Entity("LiteCharms.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Author") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationIdType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasColumnType("text"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.Property("IsInternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Platform") + .IsRequired() + .HasColumnType("text"); + + b.Property("PlatformAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("Processed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("Title") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Notification", (string)null); + }); + modelBuilder.Entity("LiteCharms.Entities.Order", b => { b.Property("Id") @@ -174,12 +230,15 @@ namespace LiteCharms.Infrastructure.Migrations b.PrimitiveCollection("Notes") .HasColumnType("jsonb"); - b.Property("ProductPriceId") + b.Property("QuoteId") .HasColumnType("uuid"); b.Property("RefundId") .HasColumnType("uuid"); + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + b.Property("Status") .HasColumnType("integer"); @@ -191,7 +250,11 @@ namespace LiteCharms.Infrastructure.Migrations b.HasIndex("CustomerId"); - b.HasIndex("ProductPriceId"); + b.HasIndex("QuoteId") + .IsUnique(); + + b.HasIndex("ShoppingCartId") + .IsUnique(); b.ToTable("Order", (string)null); }); @@ -284,6 +347,110 @@ namespace LiteCharms.Infrastructure.Migrations b.ToTable("ProductPrice", (string)null); }); + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("CustomerId1") + .HasColumnType("uuid"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("CustomerId1"); + + b.HasIndex("ShoppingCartId") + .IsUnique(); + + b.ToTable("Quote", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("QuoteId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("ShoppingCart", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.Property("Quantity") + .HasColumnType("integer"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductPriceId"); + + b.HasIndex("ShoppingCartId"); + + b.ToTable("ShoppingCartItems"); + }); + modelBuilder.Entity("LiteCharms.Entities.Lead", b => { b.HasOne("LiteCharms.Entities.Customer", "Customer") @@ -302,15 +469,22 @@ namespace LiteCharms.Infrastructure.Migrations .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") - .WithMany() - .HasForeignKey("ProductPriceId") - .OnDelete(DeleteBehavior.Restrict) + b.HasOne("LiteCharms.Entities.Quote", "Quote") + .WithOne("Order") + .HasForeignKey("LiteCharms.Entities.Order", "QuoteId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithOne("Order") + .HasForeignKey("LiteCharms.Entities.Order", "ShoppingCartId") + .OnDelete(DeleteBehavior.NoAction) .IsRequired(); b.Navigation("Customer"); - b.Navigation("ProductPrice"); + b.Navigation("Quote"); + + b.Navigation("ShoppingCart"); }); modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => @@ -335,11 +509,67 @@ namespace LiteCharms.Infrastructure.Migrations b.Navigation("Product"); }); + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.Customer", null) + .WithMany("Quotes") + .HasForeignKey("CustomerId1"); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.Navigation("Customer"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.HasOne("LiteCharms.Entities.Customer", "Customer") + .WithMany("ShoppingCarts") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.NoAction); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + { + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + .WithMany("ShoppingCartItems") + .HasForeignKey("ShoppingCartId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProductPrice"); + + b.Navigation("ShoppingCart"); + }); + modelBuilder.Entity("LiteCharms.Entities.Customer", b => { b.Navigation("Leads"); b.Navigation("Orders"); + + b.Navigation("Quotes"); + + b.Navigation("ShoppingCarts"); }); modelBuilder.Entity("LiteCharms.Entities.Order", b => @@ -351,6 +581,20 @@ namespace LiteCharms.Infrastructure.Migrations { b.Navigation("ProductPrices"); }); + + modelBuilder.Entity("LiteCharms.Entities.Quote", b => + { + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + { + b.Navigation("Order"); + + b.Navigation("Quote"); + + b.Navigation("ShoppingCartItems"); + }); #pragma warning restore 612, 618 } } diff --git a/LiteCharms.Models/Enums.cs b/LiteCharms.Models/Enums.cs index 3b26c0f..96bda99 100644 --- a/LiteCharms.Models/Enums.cs +++ b/LiteCharms.Models/Enums.cs @@ -1,5 +1,14 @@ namespace LiteCharms.Models; +public enum QuoteStatus : int +{ + Draft = 0, + Sent = 1, + Accepted = 2, + Rejected = 3, + Expired = 4 +} + public enum OrderStatus : int { Pending = 0, diff --git a/LiteCharms.Models/Notification.cs b/LiteCharms.Models/Notification.cs index 23c0596..88c4896 100644 --- a/LiteCharms.Models/Notification.cs +++ b/LiteCharms.Models/Notification.cs @@ -23,4 +23,6 @@ public class Notification public string? CorrelationIdType { get; set; } public bool IsInternal { get; set; } + + public bool Processed { get; set; } } diff --git a/LiteCharms.Models/Order.cs b/LiteCharms.Models/Order.cs index 366160e..70ec1f3 100644 --- a/LiteCharms.Models/Order.cs +++ b/LiteCharms.Models/Order.cs @@ -10,7 +10,9 @@ public class Order public Guid CustomerId { get; set; } - public Guid ProductPriceId { get; set; } + public Guid? QuoteId { get; set; } + + public Guid ShoppingCartId { get; set; } public Guid? RefundId { get; set; } diff --git a/LiteCharms.Models/Quote.cs b/LiteCharms.Models/Quote.cs new file mode 100644 index 0000000..955d0e7 --- /dev/null +++ b/LiteCharms.Models/Quote.cs @@ -0,0 +1,20 @@ +namespace LiteCharms.Models; + +public class Quote +{ + public Guid Id { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public DateTimeOffset? UpdatedAt { get; set; } + + public DateTimeOffset? ExpiredAt { get; set; } + + public Guid CustomerId { get; set; } + + public Guid ShoppingCartId { get; set; } + + public QuoteStatus Status { get; set; } + + public string? Reason { get; set; } +} diff --git a/LiteCharms.Models/ShoppingCart.cs b/LiteCharms.Models/ShoppingCart.cs new file mode 100644 index 0000000..53e27b9 --- /dev/null +++ b/LiteCharms.Models/ShoppingCart.cs @@ -0,0 +1,16 @@ +namespace LiteCharms.Models; + +public class ShoppingCart +{ + public Guid Id { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public DateTimeOffset? UpdatedAt { get; set; } + + public Guid? CustomerId { get; set; } + + public Guid? OrderId { get; set; } + + public Guid? QuoteId { get; set; } +} diff --git a/LiteCharms.Models/ShoppingCartItem.cs b/LiteCharms.Models/ShoppingCartItem.cs new file mode 100644 index 0000000..3530515 --- /dev/null +++ b/LiteCharms.Models/ShoppingCartItem.cs @@ -0,0 +1,16 @@ +namespace LiteCharms.Models; + +public class ShoppingCartItem +{ + public Guid Id { get; set; } + + public Guid ShoppingCartId { get; set; } + + public Guid ProductPriceId { get; set; } + + public DateTimeOffset CreatedAt { get; set; } + + public DateTimeOffset UpdatedAt { get; set; } + + public int Quantity { get; set; } +}