From a42c51d7b25fb910b593ee7b799d14760713dc4a Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Wed, 13 May 2026 20:06:24 +0200 Subject: [PATCH 1/5] Retructured solution --- .../LiteCharms.Abstractions.csproj | 40 --- .../Configuration/OrderConfiguration.cs | 32 -- .../Configuration/PackageConfirguration.cs | 16 - .../Configuration/ProductConfiguration.cs | 14 - .../Configuration/QuoteConfiguration.cs | 23 -- .../ShoppingCartConfiguration.cs | 31 -- .../ShoppingCartItemConfiguration.cs | 25 -- .../LiteCharms.Entities.csproj | 45 --- LiteCharms.Entities/Order.cs | 15 - LiteCharms.Entities/PackageItem.cs | 9 - LiteCharms.Extensions/Email.cs | 13 - .../LiteCharms.Extensions.csproj | 113 -------- LiteCharms.Features.Tests/CommonFixture.cs | 34 +++ .../LiteCharms.Features.Tests.csproj | 50 ++++ .../NotificationsFeatureTests.cs | 19 ++ LiteCharms.Features.Tests/appsettings.json | 22 ++ .../Abstractions}/EventBase.cs | 5 +- .../Abstractions}/IEvent.cs | 2 +- .../Handlers/SendEmailCommandHandler.cs | 61 ---- .../Email/Commands/SendEmailCommand.cs | 79 ----- .../Email/Configuration}/Account.cs | 2 +- .../Email/Configuration}/SmtpSettings.cs | 2 +- LiteCharms.Features/Email/EmailService.cs | 192 ++++++++++++ .../SendShopEmailEnquiryEventHandler.cs | 12 +- .../Email/Events/SendShopEmailEnquiryEvent.cs | 4 +- .../Email/Extensions/Constants.cs | 8 + .../Email/Extensions/EmailTelemetry.cs | 9 + LiteCharms.Features/Email/Extensions/Setup.cs | 19 ++ LiteCharms.Features/Email/IEmailService.cs | 15 + .../Email/Models/Attachment.cs | 8 + LiteCharms.Features/Email/Models/Body.cs | 26 ++ .../Email/Models/BodyProperties.cs | 8 + .../Email/Models}/EmailEnquiry.cs | 4 +- LiteCharms.Features/Email/Models/Message.cs | 19 ++ LiteCharms.Features/Email/Models/Party.cs | 8 + LiteCharms.Features/Email/Models/Response.cs | 22 ++ LiteCharms.Features/Extensions/Constants.cs | 5 + .../Extensions}/EntityModeMappers.cs | 0 LiteCharms.Features/Extensions/Hash.cs | 7 + .../Extensions}/HealthChecks.cs | 0 .../Extensions}/Monitoring.cs | 0 .../Extensions}/Postgres.cs | 0 .../Extensions}/Quartz.cs | 0 .../Extensions}/ServiceBus.cs | 0 .../Extensions}/Timezones.cs | 2 +- .../HealthChecks/PostgresHealthCheck.cs | 2 +- .../HealthChecks/QuartzHealthCheck.cs | 2 +- .../LiteCharms.Features.csproj | 95 +++++- .../Mediator/LoggingPipelineBehavior.cs | 29 ++ .../Mediator/MediatorTelemetry.cs | 12 + .../Mediator/TelemetryPipelineBehavior.cs | 66 +++++ .../Quartz/Abstractions}/IJobOrchestrator.cs | 4 +- .../Quartz/JobOrchestrator.cs | 7 +- .../Quartz/MediatorJob.cs | 4 +- .../Quartz/RetryJobListener.cs | 2 +- .../Abstractions}/EventBusQueueBase.cs | 4 +- .../ServiceBus/Abstractions}/IEventBus.cs | 4 +- .../Abstractions}/IEventBusQueue.cs | 4 +- .../ServiceBus}/Constants.cs | 6 +- .../ServiceBus/EmailServiceBus.cs | 7 +- .../ServiceBus/Exchanges/EmailExchange.cs | 33 +++ .../ServiceBus/Exchanges/GeneralExchange.cs | 4 +- .../ServiceBus/Exchanges/SalesExchange.cs | 4 +- .../ServiceBus/GeneralServiceBus.cs | 7 +- .../ServiceBus/Queues/EmailQueue.cs | 5 + .../ServiceBus/Queues/GeneralQueue.cs | 5 + .../ServiceBus/Queues/SalesQueue.cs | 5 + .../ServiceBus/SalesServiceBus.cs | 7 +- .../Commands/AddPackageItemsCommand.cs | 0 .../Commands/CreatePackageCommand.cs | 0 .../Commands/DeletePackageCommand.cs | 0 .../Commands/DeletePackageItemsCommand.cs | 0 .../Handlers/AddPackageItemCommandHandler.cs | 2 +- .../Handlers/CreatePackageCommandHandler.cs | 2 +- .../DeletePackageItemCommandHandler.cs | 2 +- .../DeletePackageItemsCommandHandler.cs | 2 +- .../Handlers/UpdatePackageCommandHandler.cs | 2 +- .../UpdatePackageStatusCommandHandler.cs | 2 +- .../Commands/UpdatePackageCommand.cs | 0 .../Commands/UpdatePackageStatusCommand.cs | 0 .../Shop/CartPackages/Entities}/Package.cs | 4 +- .../Entities/PackageConfirguration.cs | 18 ++ .../Shop/CartPackages/Entities/PackageItem.cs | 11 + .../Entities}/PackageItemConfiguration.cs | 17 +- .../Shop/CartPackages/Models}/Package.cs | 6 +- .../Shop/CartPackages/Models}/PackageItem.cs | 2 +- .../Queries/GetPackageItemsQuery.cs | 2 +- .../CartPackages/Queries/GetPackageQuery.cs | 2 +- .../CartPackages/Queries/GetPackagesQuery.cs | 2 +- .../Handlers/GetPackageItemsQueryHandler.cs | 4 +- .../Handlers/GetPackageQueryHandler.cs | 4 +- .../Handlers/GetPackagesQueryHandler.cs | 4 +- .../Commands/CreateCustomerCommand.cs | 0 .../Handlers/CreateCustomerCommandHandler.cs | 2 +- .../Handlers/UpdateCustomerCommandHandler.cs | 2 +- .../Commands/UpdateCustomerCommand.cs | 0 .../Shop/Customers/Entities}/Customer.cs | 7 +- .../Entities}/CustomerConfiguration.cs | 11 +- .../Shop/Customers/Models}/Customer.cs | 2 +- .../Customers/Queries/GetCustomerQuery.cs | 2 +- .../Customers/Queries/GetCustomersQuery.cs | 2 +- .../Handlers/GetCustomerQueryHandler.cs | 4 +- .../Handlers/GetCustomersQueryHandler.cs | 4 +- .../Shop}/Enums.cs | 17 +- .../Leads/Commands/CreateLeadCommand.cs | 0 .../Handlers/CreateLeadCommandHandler.cs | 4 +- .../Handlers/UpdateLeadCommandHandler.cs | 2 +- .../Leads/Commands/UpdateLeadCommand.cs | 3 +- .../Shop/Leads/Entities}/Lead.cs | 4 +- .../Shop/Leads/Entities}/LeadConfiguration.cs | 6 +- .../Shop/Leads/Models}/Lead.cs | 2 +- .../Leads/Queries/GetCustomerLeadsQuery.cs | 2 +- .../{ => Shop}/Leads/Queries/GetLeadsQuery.cs | 2 +- .../Handlers/GetCustomerLeadsQueryHandler.cs | 4 +- .../Queries/Handlers/GetLeadsQueryHandler.cs | 4 +- .../Commands/CreateNotificationCommand.cs | 9 +- .../CreateNotificationCommandHandler.cs | 6 +- .../UpdateNotificationCommandHandler.cs | 2 +- .../Commands/UpdateNotificationCommand.cs | 0 .../Notifications/Entities}/Notification.cs | 4 +- .../Entities}/NotificationConfiguration.cs | 8 +- .../ProcessEmailNotificationsEventHandler.cs | 7 +- .../Events/ProcessEmailNotificationsEvent.cs | 2 +- .../Notifications/INotificationService.cs | 9 + .../Notifications/Models}/Notification.cs | 2 +- .../Shop/Notifications/Models/Records.cs | 41 +++ .../Shop/Notifications/NotificationService.cs | 5 + .../Queries/GetNotificationQuery.cs | 2 +- .../Queries/GetNotificationsQuery.cs | 2 +- .../Handlers/GetNotificationQueryHandler.cs | 4 +- .../Handlers/GetNotificationsQueryHandler.cs | 4 +- .../Orders/Commands/CreateOrderCommand.cs | 0 .../Handlers/CreateOrderCommandHandler.cs | 3 +- .../UpdateOrderStatusCommandHandler.cs | 2 +- .../Commands/UpdateOrderStatusCommand.cs | 3 +- .../Shop/Orders/Entities/Order.cs | 17 ++ .../Orders/Entities/OrderConfiguration.cs | 25 ++ .../Shop/Orders/Entities}/OrderRefund.cs | 4 +- .../Entities}/OrderRefundConfiguration.cs | 12 +- .../Shop/Orders/Models}/Order.cs | 10 +- .../Shop/Orders/Models}/OrderRefund.cs | 2 +- .../Orders/Queries/GetCustomerOrdersQuery.cs | 2 +- .../Orders/Queries/GetOrderRefundQuery.cs | 2 +- .../Orders/Queries/GetOrdersQuery.cs | 2 +- .../Handlers/GetCustomerOrdersQueryHandler.cs | 4 +- .../Handlers/GetOrderRefundQueryHandler.cs | 4 +- .../Queries/Handlers/GetOrdersQueryHandler.cs | 4 +- .../Handlers/RefundCustomerCommandHandler.cs | 5 +- .../UpdateOrderRefundCommandHandler.cs | 5 +- .../Refunds/Commands/RefundCustomerCommand.cs | 2 +- .../Commands/UpdateOrderRefundCommand.cs | 2 +- .../Queries/GetCustomerRefundsQuery.cs | 4 +- .../Orders}/Refunds/Queries/GetRefundQuery.cs | 4 +- .../GetCustomerRefundsQueryHandler.cs | 8 +- .../Queries/Handlers/GetRefundQueryHandler.cs | 7 +- .../20260512065421_Init.Designer.cs | 218 +++++++------- .../Migrations/20260512065421_Init.cs | 274 +++++++++--------- .../Migrations/ShopDbContextModelSnapshot.cs | 216 +++++++------- .../Shop/Postgres}/ShopDbContext.cs | 11 +- .../Shop/Postgres}/ShopDbContextFactory.cs | 2 +- .../Shop/Products/Entities}/Product.cs | 4 +- .../Products/Entities/ProductConfiguration.cs | 17 ++ .../Shop/Products/Entities}/ProductPrice.cs | 4 +- .../Entities}/ProductPriceConfiguration.cs | 11 +- .../Shop/Products/Models/Product.cs | 18 ++ .../Shop/Products/Models}/ProductPrice.cs | 2 +- .../Products/Queries/GetProductPriceQuery.cs | 2 +- .../Products/Queries/GetProductPricesQuery.cs | 2 +- .../Products/Queries/GetProductQuery.cs | 2 +- .../Products/Queries/GetProductsQuery.cs | 2 +- .../Handlers/GetProductPriceQueryHandler.cs | 4 +- .../Handlers/GetProductPricesQueryHandler.cs | 4 +- .../Handlers/GetProductQueryHandler.cs | 4 +- .../Handlers/GetProductsQueryHandler.cs | 4 +- .../Commands/AssignQuoteToOrderCommand.cs | 0 .../AssignQuoteToShoppingCartCommand.cs | 0 .../AssignQuoteToOrderCommandHandler.cs | 2 +- ...AssignQuoteToShoppingCartCommandHandler.cs | 2 +- .../UpdateQuoteStatusCommandHandler.cs | 2 +- .../Commands/UpdateQuoteStatusCommand.cs | 3 +- .../Shop/Quotes/Entities}/Quote.cs | 10 +- .../Quotes/Entities/QuoteConfiguration.cs | 33 +++ .../Shop/Quotes/Models}/Quote.cs | 8 +- .../Quotes/Queries/GetCustomerQuotesQuery.cs | 2 +- .../Quotes/Queries/GetQuoteQuery.cs | 2 +- .../Quotes/Queries/GetQuotesQuery.cs | 2 +- .../Handlers/GetCustomerQuotesQueryHandler.cs | 4 +- .../Queries/Handlers/GetQuoteQueryHandler.cs | 4 +- .../Queries/Handlers/GetQuotesHandler.cs | 4 +- .../Commands/AddItemToShoppingCartCommand.cs | 0 .../AddPackageToShoppingCartCommand.cs | 0 .../Commands/CreateShoppingCartCommand.cs | 0 .../Commands/EmptyShoppingCartCommand.cs | 0 .../AddItemToShoppingCartCommandHandler.cs | 2 +- .../AddPackageToShoppingCartCommandHandler.cs | 2 +- .../CreateShoppingCartCommandHandler.cs | 2 +- .../EmptyShoppingCartCommandHandler.cs | 2 +- ...vePackageFromShoppingCartCommandHandler.cs | 2 +- .../RemoveShoppingCartItemCommandHandler.cs | 2 +- .../UpdateShoppingCartItemCommandHandler.cs | 2 +- .../RemovePackageFromShoppingCartCommand.cs | 0 .../Commands/RemoveShoppingCartItemCommand.cs | 0 .../Commands/UpdateShoppingCartItemCommand.cs | 0 .../ShoppingCarts/Entities}/ShoppingCart.cs | 8 +- .../Entities/ShoppingCartConfiguration.cs | 26 ++ .../Entities}/ShoppingCartItem.cs | 4 +- .../Entities/ShoppingCartItemConfiguration.cs | 30 ++ .../Entities}/ShoppingCartPackage.cs | 4 +- .../ShoppingCartPackageConfiguration.cs | 22 +- .../ShoppingCarts/Models}/ShoppingCart.cs | 6 +- .../ShoppingCarts/Models}/ShoppingCartItem.cs | 2 +- .../Models}/ShoppingCartPackage.cs | 2 +- .../Queries/GetCustomerShoppingCartsQuery.cs | 2 +- .../Queries/GetShoppingCartItemsQuery.cs | 2 +- .../Queries/GetShoppingCartPackagesQuery.cs | 2 +- .../Queries/GetShoppingCartQuery.cs | 2 +- .../GetCustomerShoppingCartsQueryHandler.cs | 4 +- .../GetShoppingCartItemsQueryHandler.cs | 4 +- .../GetShoppingCartPackagesQueryHandler.cs | 4 +- .../Handlers/GetShoppingCartQueryHandler.cs | 4 +- .../Hash/Commands/ComputeHashCommand.cs | 16 - .../Handlers/ComputeHashCommandHandler.cs | 20 -- .../LiteCharms.Infrastructure.csproj | 109 ------- .../ServiceBus/Exchanges/EmailExchange.cs | 37 --- .../ServiceBus/Queues/EmailQueue.cs | 5 - .../ServiceBus/Queues/GeneralQueue.cs | 5 - .../ServiceBus/Queues/SalesQueue.cs | 5 - LiteCharms.Infrastructure/appsettings.json | 3 - LiteCharms.Models/LiteCharms.Models.csproj | 35 --- LiteCharms.Models/Product.cs | 12 - LiteCharmsShared.slnx | 6 +- 231 files changed, 1618 insertions(+), 1408 deletions(-) delete mode 100644 LiteCharms.Abstractions/LiteCharms.Abstractions.csproj delete mode 100644 LiteCharms.Entities/Configuration/OrderConfiguration.cs delete mode 100644 LiteCharms.Entities/Configuration/PackageConfirguration.cs delete mode 100644 LiteCharms.Entities/Configuration/ProductConfiguration.cs delete mode 100644 LiteCharms.Entities/Configuration/QuoteConfiguration.cs delete mode 100644 LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs delete mode 100644 LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs delete mode 100644 LiteCharms.Entities/LiteCharms.Entities.csproj delete mode 100644 LiteCharms.Entities/Order.cs delete mode 100644 LiteCharms.Entities/PackageItem.cs delete mode 100644 LiteCharms.Extensions/Email.cs delete mode 100644 LiteCharms.Extensions/LiteCharms.Extensions.csproj create mode 100644 LiteCharms.Features.Tests/CommonFixture.cs create mode 100644 LiteCharms.Features.Tests/LiteCharms.Features.Tests.csproj create mode 100644 LiteCharms.Features.Tests/NotificationsFeatureTests.cs create mode 100644 LiteCharms.Features.Tests/appsettings.json rename {LiteCharms.Abstractions => LiteCharms.Features/Abstractions}/EventBase.cs (65%) rename {LiteCharms.Abstractions => LiteCharms.Features/Abstractions}/IEvent.cs (79%) delete mode 100644 LiteCharms.Features/Email/Commands/Handlers/SendEmailCommandHandler.cs delete mode 100644 LiteCharms.Features/Email/Commands/SendEmailCommand.cs rename {LiteCharms.Models/Configuraton/Email => LiteCharms.Features/Email/Configuration}/Account.cs (67%) rename {LiteCharms.Models/Configuraton/Email => LiteCharms.Features/Email/Configuration}/SmtpSettings.cs (77%) create mode 100644 LiteCharms.Features/Email/EmailService.cs create mode 100644 LiteCharms.Features/Email/Extensions/Constants.cs create mode 100644 LiteCharms.Features/Email/Extensions/EmailTelemetry.cs create mode 100644 LiteCharms.Features/Email/Extensions/Setup.cs create mode 100644 LiteCharms.Features/Email/IEmailService.cs create mode 100644 LiteCharms.Features/Email/Models/Attachment.cs create mode 100644 LiteCharms.Features/Email/Models/Body.cs create mode 100644 LiteCharms.Features/Email/Models/BodyProperties.cs rename {LiteCharms.Models => LiteCharms.Features/Email/Models}/EmailEnquiry.cs (86%) create mode 100644 LiteCharms.Features/Email/Models/Message.cs create mode 100644 LiteCharms.Features/Email/Models/Party.cs create mode 100644 LiteCharms.Features/Email/Models/Response.cs create mode 100644 LiteCharms.Features/Extensions/Constants.cs rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/EntityModeMappers.cs (100%) create mode 100644 LiteCharms.Features/Extensions/Hash.cs rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/HealthChecks.cs (100%) rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/Monitoring.cs (100%) rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/Postgres.cs (100%) rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/Quartz.cs (100%) rename {LiteCharms.Extensions => LiteCharms.Features/Extensions}/ServiceBus.cs (100%) rename {LiteCharms.Abstractions => LiteCharms.Features/Extensions}/Timezones.cs (96%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/HealthChecks/PostgresHealthCheck.cs (94%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/HealthChecks/QuartzHealthCheck.cs (93%) create mode 100644 LiteCharms.Features/Mediator/LoggingPipelineBehavior.cs create mode 100644 LiteCharms.Features/Mediator/MediatorTelemetry.cs create mode 100644 LiteCharms.Features/Mediator/TelemetryPipelineBehavior.cs rename {LiteCharms.Abstractions => LiteCharms.Features/Quartz/Abstractions}/IJobOrchestrator.cs (79%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/Quartz/JobOrchestrator.cs (93%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/Quartz/MediatorJob.cs (87%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/Quartz/RetryJobListener.cs (93%) rename {LiteCharms.Abstractions => LiteCharms.Features/ServiceBus/Abstractions}/EventBusQueueBase.cs (73%) rename {LiteCharms.Abstractions => LiteCharms.Features/ServiceBus/Abstractions}/IEventBus.cs (64%) rename {LiteCharms.Abstractions => LiteCharms.Features/ServiceBus/Abstractions}/IEventBusQueue.cs (56%) rename {LiteCharms.Abstractions => LiteCharms.Features/ServiceBus}/Constants.cs (55%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/ServiceBus/EmailServiceBus.cs (70%) create mode 100644 LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs rename {LiteCharms.Infrastructure => LiteCharms.Features}/ServiceBus/Exchanges/GeneralExchange.cs (69%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/ServiceBus/Exchanges/SalesExchange.cs (69%) rename {LiteCharms.Infrastructure => LiteCharms.Features}/ServiceBus/GeneralServiceBus.cs (73%) create mode 100644 LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs create mode 100644 LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs create mode 100644 LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs rename {LiteCharms.Infrastructure => LiteCharms.Features}/ServiceBus/SalesServiceBus.cs (73%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/AddPackageItemsCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/CreatePackageCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/DeletePackageCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/DeletePackageItemsCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs (95%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/UpdatePackageCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/CartPackages/Commands/UpdatePackageStatusCommand.cs (100%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/CartPackages/Entities}/Package.cs (69%) create mode 100644 LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs create mode 100644 LiteCharms.Features/Shop/CartPackages/Entities/PackageItem.cs rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/CartPackages/Entities}/PackageItemConfiguration.cs (51%) rename {LiteCharms.Models => LiteCharms.Features/Shop/CartPackages/Models}/Package.cs (66%) rename {LiteCharms.Models => LiteCharms.Features/Shop/CartPackages/Models}/PackageItem.cs (80%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/GetPackageItemsQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/GetPackageQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/GetPackagesQuery.cs (94%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs (90%) rename LiteCharms.Features/{ => Shop}/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Customers/Commands/CreateCustomerCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/Customers/Commands/UpdateCustomerCommand.cs (100%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/Customers/Entities}/Customer.cs (58%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/Customers/Entities}/CustomerConfiguration.cs (77%) rename {LiteCharms.Models => LiteCharms.Features/Shop/Customers/Models}/Customer.cs (93%) rename LiteCharms.Features/{ => Shop}/Customers/Queries/GetCustomerQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/Customers/Queries/GetCustomersQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Customers/Queries/Handlers/GetCustomerQueryHandler.cs (90%) rename LiteCharms.Features/{ => Shop}/Customers/Queries/Handlers/GetCustomersQueryHandler.cs (93%) rename {LiteCharms.Models => LiteCharms.Features/Shop}/Enums.cs (76%) rename LiteCharms.Features/{ => Shop}/Leads/Commands/CreateLeadCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/Leads/Commands/Handlers/CreateLeadCommandHandler.cs (95%) rename LiteCharms.Features/{ => Shop}/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs (95%) rename LiteCharms.Features/{ => Shop}/Leads/Commands/UpdateLeadCommand.cs (92%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/Leads/Entities}/Lead.cs (57%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/Leads/Entities}/LeadConfiguration.cs (86%) rename {LiteCharms.Models => LiteCharms.Features/Shop/Leads/Models}/Lead.cs (93%) rename LiteCharms.Features/{ => Shop}/Leads/Queries/GetCustomerLeadsQuery.cs (94%) rename LiteCharms.Features/{ => Shop}/Leads/Queries/GetLeadsQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Leads/Queries/Handlers/GetLeadsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Notifications/Commands/CreateNotificationCommand.cs (73%) rename LiteCharms.Features/{ => Shop}/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs (92%) rename LiteCharms.Features/{ => Shop}/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/Notifications/Commands/UpdateNotificationCommand.cs (100%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/Notifications/Entities}/Notification.cs (60%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/Notifications/Entities}/NotificationConfiguration.cs (88%) rename LiteCharms.Features/{ => Shop}/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs (89%) rename LiteCharms.Features/{ => Shop}/Notifications/Events/ProcessEmailNotificationsEvent.cs (91%) create mode 100644 LiteCharms.Features/Shop/Notifications/INotificationService.cs rename {LiteCharms.Models => LiteCharms.Features/Shop/Notifications/Models}/Notification.cs (93%) create mode 100644 LiteCharms.Features/Shop/Notifications/Models/Records.cs create mode 100644 LiteCharms.Features/Shop/Notifications/NotificationService.cs rename LiteCharms.Features/{ => Shop}/Notifications/Queries/GetNotificationQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/Notifications/Queries/GetNotificationsQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs (91%) rename LiteCharms.Features/{ => Shop}/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Orders/Commands/CreateOrderCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/Orders/Commands/Handlers/CreateOrderCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs (95%) rename LiteCharms.Features/{ => Shop}/Orders/Commands/UpdateOrderStatusCommand.cs (93%) create mode 100644 LiteCharms.Features/Shop/Orders/Entities/Order.cs create mode 100644 LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs rename {LiteCharms.Entities => LiteCharms.Features/Shop/Orders/Entities}/OrderRefund.cs (68%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/Orders/Entities}/OrderRefundConfiguration.cs (63%) rename {LiteCharms.Models => LiteCharms.Features/Shop/Orders/Models}/Order.cs (64%) rename {LiteCharms.Models => LiteCharms.Features/Shop/Orders/Models}/OrderRefund.cs (81%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/GetCustomerOrdersQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/GetOrderRefundQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/GetOrdersQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs (92%) rename LiteCharms.Features/{ => Shop}/Orders/Queries/Handlers/GetOrdersQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs (90%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs (85%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Commands/RefundCustomerCommand.cs (94%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Commands/UpdateOrderRefundCommand.cs (92%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Queries/GetCustomerRefundsQuery.cs (80%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Queries/GetRefundQuery.cs (80%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs (85%) rename LiteCharms.Features/{ => Shop/Orders}/Refunds/Queries/Handlers/GetRefundQueryHandler.cs (80%) rename LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.Designer.cs => LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.Designer.cs (86%) rename LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.cs => LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.cs (86%) rename {LiteCharms.Infrastructure/Database => LiteCharms.Features/Shop/Postgres}/Migrations/ShopDbContextModelSnapshot.cs (86%) rename {LiteCharms.Infrastructure/Database => LiteCharms.Features/Shop/Postgres}/ShopDbContext.cs (64%) rename {LiteCharms.Infrastructure/Database => LiteCharms.Features/Shop/Postgres}/ShopDbContextFactory.cs (93%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/Products/Entities}/Product.cs (69%) create mode 100644 LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs rename {LiteCharms.Entities => LiteCharms.Features/Shop/Products/Entities}/ProductPrice.cs (69%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/Products/Entities}/ProductPriceConfiguration.cs (70%) create mode 100644 LiteCharms.Features/Shop/Products/Models/Product.cs rename {LiteCharms.Models => LiteCharms.Features/Shop/Products/Models}/ProductPrice.cs (85%) rename LiteCharms.Features/{ => Shop}/Products/Queries/GetProductPriceQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/Products/Queries/GetProductPricesQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/Products/Queries/GetProductQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/Products/Queries/GetProductsQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/Products/Queries/Handlers/GetProductPriceQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Products/Queries/Handlers/GetProductPricesQueryHandler.cs (91%) rename LiteCharms.Features/{ => Shop}/Products/Queries/Handlers/GetProductQueryHandler.cs (90%) rename LiteCharms.Features/{ => Shop}/Products/Queries/Handlers/GetProductsQueryHandler.cs (90%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/AssignQuoteToOrderCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs (95%) rename LiteCharms.Features/{ => Shop}/Quotes/Commands/UpdateQuoteStatusCommand.cs (90%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/Quotes/Entities}/Quote.cs (54%) create mode 100644 LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs rename {LiteCharms.Models => LiteCharms.Features/Shop/Quotes/Models}/Quote.cs (64%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/GetCustomerQuotesQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/GetQuoteQuery.cs (89%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/GetQuotesQuery.cs (93%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs (91%) rename LiteCharms.Features/{ => Shop}/Quotes/Queries/Handlers/GetQuotesHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/CreateShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs (97%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs (96%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs (100%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs (100%) rename {LiteCharms.Entities => LiteCharms.Features/Shop/ShoppingCarts/Entities}/ShoppingCart.cs (54%) create mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs rename {LiteCharms.Entities => LiteCharms.Features/Shop/ShoppingCarts/Entities}/ShoppingCartItem.cs (61%) create mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs rename {LiteCharms.Entities => LiteCharms.Features/Shop/ShoppingCarts/Entities}/ShoppingCartPackage.cs (69%) rename {LiteCharms.Entities/Configuration => LiteCharms.Features/Shop/ShoppingCarts/Entities}/ShoppingCartPackageConfiguration.cs (54%) rename {LiteCharms.Models => LiteCharms.Features/Shop/ShoppingCarts/Models}/ShoppingCart.cs (64%) rename {LiteCharms.Models => LiteCharms.Features/Shop/ShoppingCarts/Models}/ShoppingCartItem.cs (83%) rename {LiteCharms.Models => LiteCharms.Features/Shop/ShoppingCarts/Models}/ShoppingCartPackage.cs (77%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/GetShoppingCartQuery.cs (90%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs (93%) rename LiteCharms.Features/{ => Shop}/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs (91%) delete mode 100644 LiteCharms.Features/Utilities/Hash/Commands/ComputeHashCommand.cs delete mode 100644 LiteCharms.Features/Utilities/Hash/Commands/Handlers/ComputeHashCommandHandler.cs delete mode 100644 LiteCharms.Infrastructure/LiteCharms.Infrastructure.csproj delete mode 100644 LiteCharms.Infrastructure/ServiceBus/Exchanges/EmailExchange.cs delete mode 100644 LiteCharms.Infrastructure/ServiceBus/Queues/EmailQueue.cs delete mode 100644 LiteCharms.Infrastructure/ServiceBus/Queues/GeneralQueue.cs delete mode 100644 LiteCharms.Infrastructure/ServiceBus/Queues/SalesQueue.cs delete mode 100644 LiteCharms.Infrastructure/appsettings.json delete mode 100644 LiteCharms.Models/LiteCharms.Models.csproj delete mode 100644 LiteCharms.Models/Product.cs diff --git a/LiteCharms.Abstractions/LiteCharms.Abstractions.csproj b/LiteCharms.Abstractions/LiteCharms.Abstractions.csproj deleted file mode 100644 index 4074ad4..0000000 --- a/LiteCharms.Abstractions/LiteCharms.Abstractions.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - net10.0 - enable - enable - True - ..\LiteCharms.snk - - - - - LiteCharms.Abstractions - 1.0.20 - Khwezi Mngoma - Lite Charms (PTY) Ltd - Shared abstractions for Lite Charms applications. - https://gitea.khongisa.co.za/litecharms/components - https://gitea.khongisa.co.za/litecharms/components.git - git - LICENSE - utility;dotnet - icon.png - - - - - - - - - - - - - - - - - diff --git a/LiteCharms.Entities/Configuration/OrderConfiguration.cs b/LiteCharms.Entities/Configuration/OrderConfiguration.cs deleted file mode 100644 index 138e136..0000000 --- a/LiteCharms.Entities/Configuration/OrderConfiguration.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace LiteCharms.Entities.Configuration; - -public class OrderConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Order)); - - builder.HasKey(f => f.Id); - 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.ShoppingCartId).IsRequired(); - builder.Property(f => f.Status).HasConversion().IsRequired(); - builder.Property(f => f.Requirements).HasColumnType("jsonb").IsRequired(false); - builder.Property(f => f.Notes).HasColumnType("jsonb").IsRequired(false); - builder.Property(f => f.Terms).HasColumnType("jsonb").IsRequired(false); - builder.Property(f => f.DepositRequired); - - builder.HasOne(f => f.Quote) - .WithOne(f => f.Order) - .HasForeignKey(f => f.QuoteId) - .OnDelete(DeleteBehavior.Restrict); - - builder.HasOne(f => f.Customer) - .WithMany(f => f.Orders) - .HasForeignKey(f => f.CustomerId) - .OnDelete(DeleteBehavior.Restrict); - } -} diff --git a/LiteCharms.Entities/Configuration/PackageConfirguration.cs b/LiteCharms.Entities/Configuration/PackageConfirguration.cs deleted file mode 100644 index 71aa4fc..0000000 --- a/LiteCharms.Entities/Configuration/PackageConfirguration.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LiteCharms.Entities.Configuration; - -public class PackageConfirguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Package)); - - builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); - builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); - builder.Property(f => f.Name).IsRequired(); - builder.Property(f => f.Description).IsRequired(); - builder.Property(f => f.Active); - } -} diff --git a/LiteCharms.Entities/Configuration/ProductConfiguration.cs b/LiteCharms.Entities/Configuration/ProductConfiguration.cs deleted file mode 100644 index 3b5ca6d..0000000 --- a/LiteCharms.Entities/Configuration/ProductConfiguration.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace LiteCharms.Entities.Configuration; - -public class ProductConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Product)); - - builder.HasKey(f => f.Id); - builder.Property(f => f.Name).IsRequired(); - builder.Property(f => f.Description).IsRequired(); - builder.Property(f => f.Active).HasDefaultValue(true); - } -} diff --git a/LiteCharms.Entities/Configuration/QuoteConfiguration.cs b/LiteCharms.Entities/Configuration/QuoteConfiguration.cs deleted file mode 100644 index d48a768..0000000 --- a/LiteCharms.Entities/Configuration/QuoteConfiguration.cs +++ /dev/null @@ -1,23 +0,0 @@ -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(false).ValueGeneratedOnUpdate(); - builder.Property(f => f.ExpiredAt).IsRequired(false); - builder.Property(f => f.CustomerId).IsRequired(); - builder.Property(f => f.Status).IsRequired().HasConversion(); - 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 deleted file mode 100644 index 109fd99..0000000 --- a/LiteCharms.Entities/Configuration/ShoppingCartConfiguration.cs +++ /dev/null @@ -1,31 +0,0 @@ -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(false).ValueGeneratedOnUpdate(); - 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 deleted file mode 100644 index 948f988..0000000 --- a/LiteCharms.Entities/Configuration/ShoppingCartItemConfiguration.cs +++ /dev/null @@ -1,25 +0,0 @@ -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(false).ValueGeneratedOnUpdate(); - 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/LiteCharms.Entities.csproj b/LiteCharms.Entities/LiteCharms.Entities.csproj deleted file mode 100644 index b32a09d..0000000 --- a/LiteCharms.Entities/LiteCharms.Entities.csproj +++ /dev/null @@ -1,45 +0,0 @@ - - - - net10.0 - enable - enable - True - ..\LiteCharms.snk - - - - - LiteCharms.Entities - 1.0.20 - Khwezi Mngoma - Lite Charms (PTY) Ltd - Shared entities for Lite Charms applications. - https://gitea.khongisa.co.za/litecharms/components - https://gitea.khongisa.co.za/litecharms/components.git - git - LICENSE - utility;dotnet - icon.png - - - - - - - - - - - - - - - - - - - - - - diff --git a/LiteCharms.Entities/Order.cs b/LiteCharms.Entities/Order.cs deleted file mode 100644 index 5f7d5dc..0000000 --- a/LiteCharms.Entities/Order.cs +++ /dev/null @@ -1,15 +0,0 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; - -[EntityTypeConfiguration] -public class Order : Models.Order -{ - public virtual OrderRefund? Refund { get; set; } - - public virtual Customer? Customer { get; set; } - - public virtual Quote? Quote { get; set; } - - public virtual ShoppingCart? ShoppingCart { get; set; } -} diff --git a/LiteCharms.Entities/PackageItem.cs b/LiteCharms.Entities/PackageItem.cs deleted file mode 100644 index 986fc75..0000000 --- a/LiteCharms.Entities/PackageItem.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; - -[EntityTypeConfiguration] -public class PackageItem : Models.PackageItem -{ - public virtual Package? Package { get; set; } -} diff --git a/LiteCharms.Extensions/Email.cs b/LiteCharms.Extensions/Email.cs deleted file mode 100644 index e081f4e..0000000 --- a/LiteCharms.Extensions/Email.cs +++ /dev/null @@ -1,13 +0,0 @@ -using LiteCharms.Models.Configuraton.Email; - -namespace LiteCharms.Extensions; - -public static class Email -{ - public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration) - { - services.Configure(configuration.GetSection("Email")); - - return services; - } -} diff --git a/LiteCharms.Extensions/LiteCharms.Extensions.csproj b/LiteCharms.Extensions/LiteCharms.Extensions.csproj deleted file mode 100644 index 3f531b7..0000000 --- a/LiteCharms.Extensions/LiteCharms.Extensions.csproj +++ /dev/null @@ -1,113 +0,0 @@ - - - - net10.0 - enable - enable - True - ..\LiteCharms.snk - true - - - - - $(NoWarn);MA0004 - - $(NoWarn);AD0001 - true - $(NoWarn);IL2080;IL2065;IL2075;IL2087;IL2057;IL2060;IL2070;IL2067;IL2072;IL2026;IL2104 - $(NoWarn);IL2110;IL2111 - - - - - LiteCharms.Extensions - 1.0.20 - Khwezi Mngoma - Lite Charms (PTY) Ltd - Extension components for Lite Charms applications. - https://gitea.khongisa.co.za/litecharms/components - https://gitea.khongisa.co.za/litecharms/components.git - git - LICENSE - utility;dotnet - icon.png - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/LiteCharms.Features.Tests/CommonFixture.cs b/LiteCharms.Features.Tests/CommonFixture.cs new file mode 100644 index 0000000..b237b8f --- /dev/null +++ b/LiteCharms.Features.Tests/CommonFixture.cs @@ -0,0 +1,34 @@ +using LiteCharms.Extensions; + +namespace LiteCharms.Features.Tests; + +public class CommonFixture : IDisposable +{ + public IConfiguration Configuration { get; set; } + + public IServiceProvider Services { get; set; } + + public IMediator Mediator { get; set; } + + public CommonFixture() + { + Configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json") + .AddUserSecrets() + .AddEnvironmentVariables() + .Build(); + + Services = new ServiceCollection() + .AddMediator() + .AddLogging() + .AddEmailServiceBus() + .AddShopDatabase(Configuration) + .AddEmailServices(Configuration) + .BuildServiceProvider(); + + Mediator = Services.GetRequiredService(); + } + + public void Dispose() { } +} diff --git a/LiteCharms.Features.Tests/LiteCharms.Features.Tests.csproj b/LiteCharms.Features.Tests/LiteCharms.Features.Tests.csproj new file mode 100644 index 0000000..7070851 --- /dev/null +++ b/LiteCharms.Features.Tests/LiteCharms.Features.Tests.csproj @@ -0,0 +1,50 @@ + + + + net10.0 + enable + enable + false + 62fa604a-1340-4edb-9ddd-3305fcf46fca + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + \ No newline at end of file diff --git a/LiteCharms.Features.Tests/NotificationsFeatureTests.cs b/LiteCharms.Features.Tests/NotificationsFeatureTests.cs new file mode 100644 index 0000000..53b8154 --- /dev/null +++ b/LiteCharms.Features.Tests/NotificationsFeatureTests.cs @@ -0,0 +1,19 @@ +using LiteCharms.Features.Notifications.Commands; + +namespace LiteCharms.Features.Tests; + +public class NotificationsFeatureTests(CommonFixture fixture) : IClassFixture +{ + [Fact] + public async Task CreateNotificationCommand_ShouldSucceed() + { + var command = CreateNotification.Create(Models.NotificationDirection.Outgoing, "UnitTest", "khwezi@mngoma.co.za", + "CreateNotificationCommand_ShouldSucceed Test", "Test Message", Models.NotificationPlatforms.Email, Models.Priorities.Medium, + "Khngisa Shop - Test", "shop@litecharms.co.za", Guid.NewGuid().ToString(), Models.CorrelationIdTypes.None, + true, false); + + var result = await fixture.Mediator.Send(command); + + Assert.True(result.IsSuccess); + } +} diff --git a/LiteCharms.Features.Tests/appsettings.json b/LiteCharms.Features.Tests/appsettings.json new file mode 100644 index 0000000..aec5c2e --- /dev/null +++ b/LiteCharms.Features.Tests/appsettings.json @@ -0,0 +1,22 @@ +{ + "Email": { + "Credentials": { + "Username": "shop@litecharms.co.za" + }, + "Port": 465, + "Host": "mail.litecharms.co.za", + "UseSsl": true + }, + "Monitoring": { + "ApiKey": "", + "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", + "ServiceName": "LiteCharms.LeadGenerator" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/LiteCharms.Abstractions/EventBase.cs b/LiteCharms.Features/Abstractions/EventBase.cs similarity index 65% rename from LiteCharms.Abstractions/EventBase.cs rename to LiteCharms.Features/Abstractions/EventBase.cs index 6c11736..32def99 100644 --- a/LiteCharms.Abstractions/EventBase.cs +++ b/LiteCharms.Features/Abstractions/EventBase.cs @@ -1,6 +1,7 @@ -using static LiteCharms.Abstractions.Timezones; +using LiteCharms.Features.Extensions; +using static LiteCharms.Features.Extensions.Timezones; -namespace LiteCharms.Abstractions; +namespace LiteCharms.Features.Abstractions; public abstract class EventBase { diff --git a/LiteCharms.Abstractions/IEvent.cs b/LiteCharms.Features/Abstractions/IEvent.cs similarity index 79% rename from LiteCharms.Abstractions/IEvent.cs rename to LiteCharms.Features/Abstractions/IEvent.cs index 08bc091..366ad86 100644 --- a/LiteCharms.Abstractions/IEvent.cs +++ b/LiteCharms.Features/Abstractions/IEvent.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Abstractions; +namespace LiteCharms.Features.Abstractions; public interface IEvent : INotification { diff --git a/LiteCharms.Features/Email/Commands/Handlers/SendEmailCommandHandler.cs b/LiteCharms.Features/Email/Commands/Handlers/SendEmailCommandHandler.cs deleted file mode 100644 index 30f86dc..0000000 --- a/LiteCharms.Features/Email/Commands/Handlers/SendEmailCommandHandler.cs +++ /dev/null @@ -1,61 +0,0 @@ -using LiteCharms.Features.Email.Commands; -using LiteCharms.Models.Configuraton.Email; - -namespace LiteCharms.Features.Email.Commands.Handlers; - -public class SendEmailCommandHandler(IOptions smtpOptions) : IRequestHandler -{ - public async ValueTask Handle(SendEmailCommand request, CancellationToken cancellationToken) - { - try - { - var settings = smtpOptions.Value; - - if(settings == null) - return Result.Fail(new Error("SMTP settings are not configured.")); - - if(settings.Credentials == null) - return Result.Fail(new Error("SMTP credentials are not configured.")); - - if(string.IsNullOrWhiteSpace(settings?.Credentials.Username) || string.IsNullOrWhiteSpace(settings.Credentials.Password)) - return Result.Fail(new Error("SMTP credentials are incomplete.")); - - if(string.IsNullOrWhiteSpace(settings.Host) || settings.Port == 0) - return Result.Fail(new Error("SMTP host and port must be configured.")); - - var message = new MimeMessage(); - message.From.Add(new MailboxAddress(request.SenderName, request.From!)); - message.To.Add(new MailboxAddress(request.RecipientName, request.To!)); - message.Subject = request.Subject!; - - var bodyBuilder = new BodyBuilder(); - - if(request.Attachment?.Length > 0 && !string.IsNullOrEmpty(request.AttachmentFileName)) - bodyBuilder.Attachments.Add(request.AttachmentFileName!, request.Attachment!, cancellationToken); - - if (!request.IsHtml) bodyBuilder.TextBody = request.Message; - if (request.IsHtml) bodyBuilder.HtmlBody = request.Message; - - message.Body = bodyBuilder.ToMessageBody(); - - using var client = new SmtpClient(); - - await client.ConnectAsync(settings.Host!, settings.Port, settings.UseSsl, cancellationToken); - await client.AuthenticateAsync(settings.Credentials!.Username!, settings.Credentials.Password!, cancellationToken); - - var response = await client.SendAsync(message, cancellationToken); - - bool emailSent = response.Contains("OK", StringComparison.InvariantCultureIgnoreCase); - - await client.DisconnectAsync(true, cancellationToken); - - return emailSent - ? Result.Ok() - : Result.Fail(new Error("Failed to send email. SMTP response: " + response)); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Email/Commands/SendEmailCommand.cs b/LiteCharms.Features/Email/Commands/SendEmailCommand.cs deleted file mode 100644 index 972e496..0000000 --- a/LiteCharms.Features/Email/Commands/SendEmailCommand.cs +++ /dev/null @@ -1,79 +0,0 @@ -namespace LiteCharms.Features.Email.Commands; - -public class SendEmailCommand : IRequest -{ - public string? From { get; set; } - - public string? SenderName { get; set; } - - public string? To { get; set; } - - public string? RecipientName { get; set; } - - public string? Subject { get; set; } - - public string? Message { get; set; } - - public bool IsHtml { get; set; } - - public Stream? Attachment { get; set; } - - public string? AttachmentFileName { get; set; } - - private SendEmailCommand(string from, string senderName, string to, string recipientName, string subject, string message, bool isHtml = false, Stream? attachment = null, string? attachmentFileName = null) - { - From = from; - To = to; - Subject = subject; - Message = message; - IsHtml = isHtml; - Attachment = attachment; - AttachmentFileName = attachmentFileName; - SenderName = senderName; - RecipientName = recipientName; - } - - public static SendEmailCommand Create(string from, string senderName, string to, string recipientName, string subject, string message, bool isHtml = false, Stream? attachment = null, string? attachmentFileName = null) - { - if (string.IsNullOrWhiteSpace(from)) - throw new ArgumentException("From address is required."); - - if (string.IsNullOrWhiteSpace(senderName)) - throw new ArgumentException("Sender name is required."); - - if (!string.IsNullOrWhiteSpace(senderName) && senderName?.Length > 255) - throw new ArgumentException("Sender name cannot exceed 255 characters."); - - if (string.IsNullOrWhiteSpace(to)) - throw new ArgumentException("To address is required."); - - if (string.IsNullOrWhiteSpace(recipientName)) - throw new ArgumentException("Recipient name is required."); - - if (!string.IsNullOrWhiteSpace(recipientName) && recipientName?.Length > 255) - throw new ArgumentException("Recipient name cannot exceed 255 characters."); - - if (string.IsNullOrWhiteSpace(subject)) - throw new ArgumentException("Subject is required."); - - if (!string.IsNullOrWhiteSpace(subject) && subject?.Length > 2048) - throw new ArgumentException("Subject cannot exceed 2048 characters."); - - if (string.IsNullOrWhiteSpace(message)) - throw new ArgumentException("Message is required."); - - if (message.Length > 10485760) - throw new ArgumentException("Message cannot exceed 10 MB."); - - if (attachment != null && string.IsNullOrWhiteSpace(attachmentFileName)) - throw new ArgumentException("Attachment file name must be provided when an attachment is included."); - - if (attachment is not null && attachment.Length > 10485760) - throw new ArgumentException("Attachment cannot exceed 10 MB."); - - if (!string.IsNullOrWhiteSpace(attachmentFileName) && attachmentFileName.Length > 255) - throw new ArgumentException("Attachment file name cannot exceed 255 characters."); - - return new(from, senderName!, to, recipientName!, subject!, message, isHtml, attachment, attachmentFileName); - } -} diff --git a/LiteCharms.Models/Configuraton/Email/Account.cs b/LiteCharms.Features/Email/Configuration/Account.cs similarity index 67% rename from LiteCharms.Models/Configuraton/Email/Account.cs rename to LiteCharms.Features/Email/Configuration/Account.cs index 96424ce..e0c65e7 100644 --- a/LiteCharms.Models/Configuraton/Email/Account.cs +++ b/LiteCharms.Features/Email/Configuration/Account.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models.Configuraton.Email; +namespace LiteCharms.Features.Email.Configuration; public class Account { diff --git a/LiteCharms.Models/Configuraton/Email/SmtpSettings.cs b/LiteCharms.Features/Email/Configuration/SmtpSettings.cs similarity index 77% rename from LiteCharms.Models/Configuraton/Email/SmtpSettings.cs rename to LiteCharms.Features/Email/Configuration/SmtpSettings.cs index c44fbbe..78798f2 100644 --- a/LiteCharms.Models/Configuraton/Email/SmtpSettings.cs +++ b/LiteCharms.Features/Email/Configuration/SmtpSettings.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models.Configuraton.Email; +namespace LiteCharms.Features.Email.Configuration; public class SmtpSettings { diff --git a/LiteCharms.Features/Email/EmailService.cs b/LiteCharms.Features/Email/EmailService.cs new file mode 100644 index 0000000..8cb3a82 --- /dev/null +++ b/LiteCharms.Features/Email/EmailService.cs @@ -0,0 +1,192 @@ +using LiteCharms.Features.Email.Configuration; +using LiteCharms.Features.Email.Extensions; +using LiteCharms.Features.Email.Models; +using LiteCharms.Features.Shop; + +namespace LiteCharms.Features.Email; + +public class EmailService(IOptions options) : IEmailService +{ + private readonly SmtpSettings settings = options.Value; + private readonly SmtpClient client = new(); + private readonly int sendMaxCount = 10; + private int sendCount = 0; + + public EmailStatuses Status { get; private set; } = EmailStatuses.Disconnected; + + public async Task> SendEmailAsync(Message message, CancellationToken cancellationToken = default) + { + using var activity = EmailTelemetry.Source.StartActivity("Email Send"); + activity?.SetTag("email.recipient", message.Recipient?.Address); + + try + { + if (Status != EmailStatuses.Connected) + { + activity?.SetStatus(ActivityStatusCode.Error, "Disconnected"); + + return Result.Fail("Smtp service is disconnected."); + } + + var email = new MimeMessage(); + email.From.Add(new MailboxAddress(message.Sender!.Name, message.Sender.Address!)); + email.To.Add(new MailboxAddress(message.Recipient!.Name, message.Recipient!.Address!)); + email.Subject = message.Subject!; + + var bodyBuilder = new BodyBuilder(); + + foreach (var attachment in message.Body?.Attachments!) + bodyBuilder.Attachments.Add(attachment.Name!, attachment.FileStream!, cancellationToken); + + if (!message.Body.Properties.IsHtml) bodyBuilder.TextBody = message.Body.Message; + if (message.Body.Properties.IsHtml) bodyBuilder.HtmlBody = message.Body.Message; + + email.Body = bodyBuilder.ToMessageBody(); + + var response = await client.SendAsync(email, cancellationToken); + + bool emailSent = response.Contains("OK", StringComparison.InvariantCultureIgnoreCase); + + message.Dispose(); + + Interlocked.Increment(ref sendCount); + + if (sendCount % sendMaxCount == 0) + { + using var delayActivity = EmailTelemetry.Source.StartActivity("Rate Limit Pause"); + + sendCount = 0; + + await Task.Delay(1000, cancellationToken); + } + + if (emailSent) + { + EmailTelemetry.EmailsSent.Add(1, new TagList { { "host", settings.Host } }); + + return Result.Ok(Response.Create(EmailStatuses.Success)); + } + + await DisconnectAsync(cancellationToken); + + if (response.Contains("421")) + { + Status = EmailStatuses.TooManyConnections; + + return Result.Fail(response); + } + + if (response.Contains("451")) + { + Status = EmailStatuses.ConnectionAborted; + + return Result.Fail(response); + } + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "error_message", response } }); + + Status = EmailStatuses.Disconnected; + + return Result.Fail("General error, disconnected"); + } + catch (Exception ex) + { + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity?.AddException(ex); + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } }); + + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task> ConnectAsync(CancellationToken cancellationToken = default) + { + using var activity = EmailTelemetry.Source.StartActivity("Email Connect"); + activity?.SetTag("email.smtp.connect", settings.Host); + + try + { + if (Status is EmailStatuses.Connected) return Result.Ok(Response.Create(Status)); + + await client.ConnectAsync(settings.Host!, settings.Port, settings.UseSsl, cancellationToken); + await client.AuthenticateAsync(settings.Credentials!.Username!, settings.Credentials.Password!, cancellationToken); + + Status = EmailStatuses.Connected; + + activity?.SetStatus(ActivityStatusCode.Ok, "Connected"); + + return Result.Ok(Response.Create(Status)); + } + catch (MailKit.ProtocolException ex) + { + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity?.AddException(ex); + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } }); + + Status = EmailStatuses.ProtocolError; + + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + catch (Exception ex) when (ex is MailKit.Security.SslHandshakeException || ex is MailKit.Security.AuthenticationException) + { + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity?.AddException(ex); + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } }); + + Status = EmailStatuses.AuthenticationError; + + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + catch (Exception ex) + { + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity?.AddException(ex); + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } }); + + Status = EmailStatuses.GeneralError; + + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async Task DisconnectAsync(CancellationToken cancellationToken = default) + { + using var activity = EmailTelemetry.Source.StartActivity("Email Disconnect"); + activity?.SetTag("email.smtp.disconnect", settings.Host); + + try + { + if (Status is EmailStatuses.Disconnected) return Result.Ok(); + + await client.DisconnectAsync(true, cancellationToken); + + activity?.SetStatus(ActivityStatusCode.Ok, "Disconnected"); + + Status = EmailStatuses.Disconnected; + + return Result.Ok(); + } + catch (Exception ex) + { + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + activity?.AddException(ex); + + EmailTelemetry.EmailsFailed.Add(1, new TagList { { "exception", ex.GetType().Name } }); + + Status = EmailStatuses.GeneralError; + + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public void Dispose() + { + client.Dispose(); + + GC.SuppressFinalize(this); + } +} diff --git a/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs b/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs index 9c19500..f3e703f 100644 --- a/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs +++ b/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs @@ -1,18 +1,22 @@ using LiteCharms.Features.Notifications.Commands; -using static LiteCharms.Abstractions.Constants; +using LiteCharms.Features.Shop; +using static LiteCharms.Features.Email.Extensions.Constants; namespace LiteCharms.Features.Email.Events.Handlers; +// TODO: Inject the INotificationService public class SendShopEmailEnquiryEventHandler(ISender mediator) : INotificationHandler { public async ValueTask Handle(SendShopEmailEnquiryEvent notification, CancellationToken cancellationToken) { - var command = CreateNotificationCommand.Create(Models.NotificationDirection.Outgoing, notification.SenderName!, - notification.SenderAddress!, notification.Subject!, notification.Message!, Models.NotificationPlatforms.Email, + // TODO: Refactor this to use the NotificationService + var command = CreateNotification.Create(NotificationDirection.Outgoing, notification.SenderName!, + notification.SenderAddress!, notification.Subject!, notification.Message!, NotificationPlatforms.Email, notification.Priority, ShopEmailFromName, ShopEmailFromAddress, Guid.CreateVersion7().ToString(), - Models.CorrelationIdTypes.None, isInternal: true, isHtml: false); + CorrelationIdTypes.None, isInternal: true, isHtml: false); + // TODO: Remove, deprecated await mediator.Send(command, cancellationToken); } } diff --git a/LiteCharms.Features/Email/Events/SendShopEmailEnquiryEvent.cs b/LiteCharms.Features/Email/Events/SendShopEmailEnquiryEvent.cs index 07e3830..3377b44 100644 --- a/LiteCharms.Features/Email/Events/SendShopEmailEnquiryEvent.cs +++ b/LiteCharms.Features/Email/Events/SendShopEmailEnquiryEvent.cs @@ -1,5 +1,5 @@ -using LiteCharms.Abstractions; -using LiteCharms.Models; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.Shop; namespace LiteCharms.Features.Email.Events; diff --git a/LiteCharms.Features/Email/Extensions/Constants.cs b/LiteCharms.Features/Email/Extensions/Constants.cs new file mode 100644 index 0000000..1d02879 --- /dev/null +++ b/LiteCharms.Features/Email/Extensions/Constants.cs @@ -0,0 +1,8 @@ +namespace LiteCharms.Features.Email.Extensions; + +public static class Constants +{ + public const string ShopSchedulerName = "shop"; + public const string ShopEmailFromName = "Khongisa Shop"; + public const string ShopEmailFromAddress = "shop@litecharms.co.za"; +} diff --git a/LiteCharms.Features/Email/Extensions/EmailTelemetry.cs b/LiteCharms.Features/Email/Extensions/EmailTelemetry.cs new file mode 100644 index 0000000..48a9a4a --- /dev/null +++ b/LiteCharms.Features/Email/Extensions/EmailTelemetry.cs @@ -0,0 +1,9 @@ +namespace LiteCharms.Features.Email.Extensions; + +public static class EmailTelemetry +{ + public static readonly ActivitySource Source = new("LiteCharms.EmailService"); + public static readonly Meter Meter = new("LiteCharms.EmailService"); + public static readonly Counter EmailsSent = Meter.CreateCounter("emails_sent_total", "count", "Total successful emails sent"); + public static readonly Counter EmailsFailed = Meter.CreateCounter("emails_failed_total", "count", "Total failed email attempts"); +} diff --git a/LiteCharms.Features/Email/Extensions/Setup.cs b/LiteCharms.Features/Email/Extensions/Setup.cs new file mode 100644 index 0000000..f937dda --- /dev/null +++ b/LiteCharms.Features/Email/Extensions/Setup.cs @@ -0,0 +1,19 @@ +using LiteCharms.Features.Email.Configuration; + +namespace LiteCharms.Features.Email.Extensions; + +public static class Setup +{ + public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration) + { + services.Configure(configuration.GetSection("Email")); + + services.AddSingleton(); + + services.AddOpenTelemetry() + .WithTracing(tracing => tracing.AddSource("LiteCharms.EmailService")) + .WithMetrics(metrics => metrics.AddMeter("LiteCharms.EmailService")); + + return services; + } +} diff --git a/LiteCharms.Features/Email/IEmailService.cs b/LiteCharms.Features/Email/IEmailService.cs new file mode 100644 index 0000000..bda6a6f --- /dev/null +++ b/LiteCharms.Features/Email/IEmailService.cs @@ -0,0 +1,15 @@ +using LiteCharms.Features.Email.Models; +using LiteCharms.Features.Shop; + +namespace LiteCharms.Features.Email; + +public interface IEmailService : IDisposable +{ + EmailStatuses Status { get; } + + Task> SendEmailAsync(Message message, CancellationToken cancellationToken = default); + + Task> ConnectAsync(CancellationToken cancellationToken = default); + + Task DisconnectAsync(CancellationToken cancellationToken = default); +} diff --git a/LiteCharms.Features/Email/Models/Attachment.cs b/LiteCharms.Features/Email/Models/Attachment.cs new file mode 100644 index 0000000..6558058 --- /dev/null +++ b/LiteCharms.Features/Email/Models/Attachment.cs @@ -0,0 +1,8 @@ +namespace LiteCharms.Features.Email.Models; + +public class Attachment +{ + public string? Name { get; set; } + + public Stream? FileStream { get; set; } +} diff --git a/LiteCharms.Features/Email/Models/Body.cs b/LiteCharms.Features/Email/Models/Body.cs new file mode 100644 index 0000000..99156d8 --- /dev/null +++ b/LiteCharms.Features/Email/Models/Body.cs @@ -0,0 +1,26 @@ +namespace LiteCharms.Features.Email.Models; + +public class Body : IDisposable +{ + public string? Message { get; set; } + + public ReadOnlyCollection? Attachments { get; set; } + + public BodyProperties Properties { get; set; } = new(); + + public void Dispose() + { + if (Attachments is null) return; + + foreach (var attachment in Attachments!) + { + if (attachment is not null) + { + attachment.FileStream!.Close(); + attachment.FileStream!.Dispose(); + } + } + + GC.SuppressFinalize(this); + } +} diff --git a/LiteCharms.Features/Email/Models/BodyProperties.cs b/LiteCharms.Features/Email/Models/BodyProperties.cs new file mode 100644 index 0000000..7bdfa01 --- /dev/null +++ b/LiteCharms.Features/Email/Models/BodyProperties.cs @@ -0,0 +1,8 @@ +namespace LiteCharms.Features.Email.Models; + +public class BodyProperties +{ + public bool IsHtml { get; set; } + + public bool HasAttachments { get; set; } +} diff --git a/LiteCharms.Models/EmailEnquiry.cs b/LiteCharms.Features/Email/Models/EmailEnquiry.cs similarity index 86% rename from LiteCharms.Models/EmailEnquiry.cs rename to LiteCharms.Features/Email/Models/EmailEnquiry.cs index a858328..97c2fe3 100644 --- a/LiteCharms.Models/EmailEnquiry.cs +++ b/LiteCharms.Features/Email/Models/EmailEnquiry.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Models; +using System.ComponentModel.DataAnnotations; + +namespace LiteCharms.Features.Email.Models; public sealed class EmailEnquiry { diff --git a/LiteCharms.Features/Email/Models/Message.cs b/LiteCharms.Features/Email/Models/Message.cs new file mode 100644 index 0000000..7432545 --- /dev/null +++ b/LiteCharms.Features/Email/Models/Message.cs @@ -0,0 +1,19 @@ +namespace LiteCharms.Features.Email.Models; + +public class Message : IDisposable +{ + public Party? Sender { get; set; } + + public Party? Recipient { get; set; } + + public string? Subject { get; set; } + + public Body? Body { get; set; } + + public void Dispose() + { + Body?.Dispose(); + + GC.SuppressFinalize(this); + } +} diff --git a/LiteCharms.Features/Email/Models/Party.cs b/LiteCharms.Features/Email/Models/Party.cs new file mode 100644 index 0000000..65c2e85 --- /dev/null +++ b/LiteCharms.Features/Email/Models/Party.cs @@ -0,0 +1,8 @@ +namespace LiteCharms.Features.Email.Models; + +public class Party +{ + public string? Name { get; set; } + + public string? Address { get; set; } +} diff --git a/LiteCharms.Features/Email/Models/Response.cs b/LiteCharms.Features/Email/Models/Response.cs new file mode 100644 index 0000000..4474d9e --- /dev/null +++ b/LiteCharms.Features/Email/Models/Response.cs @@ -0,0 +1,22 @@ +using LiteCharms.Features.Shop; + +namespace LiteCharms.Features.Email.Models; + +public class Response +{ + public int Code { get; set; } + + public string? Error { get; set; } + + public EmailStatuses Status { get; set; } + + private Response(EmailStatuses status, int code = 0, string? error = null) + { + Status = status; + Code = code; + Error = error; + } + + public static Response Create(EmailStatuses status, int code = 0, string? error = null) => + new(status, code, error); +} diff --git a/LiteCharms.Features/Extensions/Constants.cs b/LiteCharms.Features/Extensions/Constants.cs new file mode 100644 index 0000000..b9d11e1 --- /dev/null +++ b/LiteCharms.Features/Extensions/Constants.cs @@ -0,0 +1,5 @@ +namespace LiteCharms.Features.Extensions; + +public static class Constants +{ +} diff --git a/LiteCharms.Extensions/EntityModeMappers.cs b/LiteCharms.Features/Extensions/EntityModeMappers.cs similarity index 100% rename from LiteCharms.Extensions/EntityModeMappers.cs rename to LiteCharms.Features/Extensions/EntityModeMappers.cs diff --git a/LiteCharms.Features/Extensions/Hash.cs b/LiteCharms.Features/Extensions/Hash.cs new file mode 100644 index 0000000..ab76ecc --- /dev/null +++ b/LiteCharms.Features/Extensions/Hash.cs @@ -0,0 +1,7 @@ +namespace LiteCharms.Features.Extensions; + +public static class Hash +{ + public static Func GenerateSha256HashString = (input) => + Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(input!))); +} diff --git a/LiteCharms.Extensions/HealthChecks.cs b/LiteCharms.Features/Extensions/HealthChecks.cs similarity index 100% rename from LiteCharms.Extensions/HealthChecks.cs rename to LiteCharms.Features/Extensions/HealthChecks.cs diff --git a/LiteCharms.Extensions/Monitoring.cs b/LiteCharms.Features/Extensions/Monitoring.cs similarity index 100% rename from LiteCharms.Extensions/Monitoring.cs rename to LiteCharms.Features/Extensions/Monitoring.cs diff --git a/LiteCharms.Extensions/Postgres.cs b/LiteCharms.Features/Extensions/Postgres.cs similarity index 100% rename from LiteCharms.Extensions/Postgres.cs rename to LiteCharms.Features/Extensions/Postgres.cs diff --git a/LiteCharms.Extensions/Quartz.cs b/LiteCharms.Features/Extensions/Quartz.cs similarity index 100% rename from LiteCharms.Extensions/Quartz.cs rename to LiteCharms.Features/Extensions/Quartz.cs diff --git a/LiteCharms.Extensions/ServiceBus.cs b/LiteCharms.Features/Extensions/ServiceBus.cs similarity index 100% rename from LiteCharms.Extensions/ServiceBus.cs rename to LiteCharms.Features/Extensions/ServiceBus.cs diff --git a/LiteCharms.Abstractions/Timezones.cs b/LiteCharms.Features/Extensions/Timezones.cs similarity index 96% rename from LiteCharms.Abstractions/Timezones.cs rename to LiteCharms.Features/Extensions/Timezones.cs index 5457130..bbddd5d 100644 --- a/LiteCharms.Abstractions/Timezones.cs +++ b/LiteCharms.Features/Extensions/Timezones.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Abstractions; +namespace LiteCharms.Features.Extensions; public static class Timezones { diff --git a/LiteCharms.Infrastructure/HealthChecks/PostgresHealthCheck.cs b/LiteCharms.Features/HealthChecks/PostgresHealthCheck.cs similarity index 94% rename from LiteCharms.Infrastructure/HealthChecks/PostgresHealthCheck.cs rename to LiteCharms.Features/HealthChecks/PostgresHealthCheck.cs index 9cb5f03..377da08 100644 --- a/LiteCharms.Infrastructure/HealthChecks/PostgresHealthCheck.cs +++ b/LiteCharms.Features/HealthChecks/PostgresHealthCheck.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Infrastructure.HealthChecks; +namespace LiteCharms.Features.HealthChecks; public class PostgresHealthCheck(IConfiguration configuration) : IHealthCheck { diff --git a/LiteCharms.Infrastructure/HealthChecks/QuartzHealthCheck.cs b/LiteCharms.Features/HealthChecks/QuartzHealthCheck.cs similarity index 93% rename from LiteCharms.Infrastructure/HealthChecks/QuartzHealthCheck.cs rename to LiteCharms.Features/HealthChecks/QuartzHealthCheck.cs index d1a8bd0..59eb397 100644 --- a/LiteCharms.Infrastructure/HealthChecks/QuartzHealthCheck.cs +++ b/LiteCharms.Features/HealthChecks/QuartzHealthCheck.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Infrastructure.HealthChecks; +namespace LiteCharms.Features.HealthChecks; public class QuartzHealthCheck(ISchedulerFactory schedulerFactory) : IHealthCheck { diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index f541547..3d6552b 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -28,6 +28,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + @@ -50,16 +130,17 @@ + - - + + + + + + + - - - - - diff --git a/LiteCharms.Features/Mediator/LoggingPipelineBehavior.cs b/LiteCharms.Features/Mediator/LoggingPipelineBehavior.cs new file mode 100644 index 0000000..2bf0410 --- /dev/null +++ b/LiteCharms.Features/Mediator/LoggingPipelineBehavior.cs @@ -0,0 +1,29 @@ +namespace LiteCharms.Features.Mediator; + +public sealed class LoggingPipelineBehavior(ILogger> logger) : + IPipelineBehavior + where TRequest : IRequest + where TResponse : ResultBase, new() +{ + public async ValueTask Handle(TRequest message, MessageHandlerDelegate next, CancellationToken cancellationToken) + { + TResponse? response = await next(message, cancellationToken); + + if (response is null) + logger.LogCritical("{Request} {TypeName} was returned as null", typeof(TRequest).Name, typeof(TRequest).Name); + + if(response?.IsFailed == true || response?.Errors?.Any() == true) + { + foreach (var error in response.Errors) + { + if (!string.IsNullOrWhiteSpace(error.Message)) + logger.LogWarning("{Request} {Error}", typeof(TRequest).Name, error.Message); + + if (error?.Reasons?.Count > 0) + error.Reasons.ForEach(r => logger.LogError("{Request} {Reason}", typeof(TRequest).Name, r.ToString())); + } + } + + return response; + } +} diff --git a/LiteCharms.Features/Mediator/MediatorTelemetry.cs b/LiteCharms.Features/Mediator/MediatorTelemetry.cs new file mode 100644 index 0000000..00493c1 --- /dev/null +++ b/LiteCharms.Features/Mediator/MediatorTelemetry.cs @@ -0,0 +1,12 @@ +namespace LiteCharms.Features.Mediator; + +public static class MediatorTelemetry +{ + public const string ServiceName = "LiteCharms.Mediator"; + + public static readonly ActivitySource Source = new(ServiceName); + public static readonly Meter Meter = new(ServiceName); + + public static readonly Counter RequestCounter = Meter.CreateCounter("mediator_requests_total"); + public static readonly Histogram RequestDuration = Meter.CreateHistogram("mediator_request_duration_ms"); +} diff --git a/LiteCharms.Features/Mediator/TelemetryPipelineBehavior.cs b/LiteCharms.Features/Mediator/TelemetryPipelineBehavior.cs new file mode 100644 index 0000000..0a9efbd --- /dev/null +++ b/LiteCharms.Features/Mediator/TelemetryPipelineBehavior.cs @@ -0,0 +1,66 @@ +namespace LiteCharms.Features.Mediator; + +public sealed class TelemetryPipelineBehavior : + IPipelineBehavior + where TRequest : IRequest + where TResponse : ResultBase, new() +{ + public async ValueTask Handle(TRequest message, MessageHandlerDelegate next, CancellationToken cancellationToken) + { + var requestName = typeof(TRequest).Name; + + using var activity = MediatorTelemetry.Source.StartActivity(requestName); + + activity?.SetTag("mediator.request_type", typeof(TRequest).FullName); + + var stopWatch = Stopwatch.StartNew(); + var status = "Success"; + + try + { + TResponse? response = await next(message, cancellationToken); + + if (response is null) + { + status = "NullResponse"; + activity?.SetStatus(ActivityStatusCode.Error, "Response was null"); + + return response; + } + + if (response.IsFailed) + { + status = "Failed"; + activity?.SetStatus(ActivityStatusCode.Error, "Request failed"); + + var firstError = response.Errors.FirstOrDefault()?.Message ?? "Unknown Error"; + activity?.SetTag("error.message", firstError); + + foreach (var error in response.Errors) + activity?.AddEvent(new ActivityEvent("Result Error", tags: new() { { "message", error.Message } })); + } + else + activity?.SetStatus(ActivityStatusCode.Ok); + + return response; + } + catch (Exception ex) + { + status = "Exception"; + activity?.SetStatus(ActivityStatusCode.Error, ex.Message); + + activity?.AddException(ex); + + throw; + } + finally + { + stopWatch.Stop(); + + var tags = new TagList { { "request", requestName }, { "status", status } }; + + MediatorTelemetry.RequestCounter.Add(1, tags); + MediatorTelemetry.RequestDuration.Record(stopWatch.Elapsed.TotalMilliseconds, tags); + } + } +} \ No newline at end of file diff --git a/LiteCharms.Abstractions/IJobOrchestrator.cs b/LiteCharms.Features/Quartz/Abstractions/IJobOrchestrator.cs similarity index 79% rename from LiteCharms.Abstractions/IJobOrchestrator.cs rename to LiteCharms.Features/Quartz/Abstractions/IJobOrchestrator.cs index d98c155..8ce2c33 100644 --- a/LiteCharms.Abstractions/IJobOrchestrator.cs +++ b/LiteCharms.Features/Quartz/Abstractions/IJobOrchestrator.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; + +namespace LiteCharms.Features.Quartz.Abstractions; public interface IJobOrchestrator { diff --git a/LiteCharms.Infrastructure/Quartz/JobOrchestrator.cs b/LiteCharms.Features/Quartz/JobOrchestrator.cs similarity index 93% rename from LiteCharms.Infrastructure/Quartz/JobOrchestrator.cs rename to LiteCharms.Features/Quartz/JobOrchestrator.cs index 6874541..4f578b5 100644 --- a/LiteCharms.Infrastructure/Quartz/JobOrchestrator.cs +++ b/LiteCharms.Features/Quartz/JobOrchestrator.cs @@ -1,7 +1,8 @@ -using LiteCharms.Abstractions; -using static LiteCharms.Abstractions.Timezones; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.Quartz.Abstractions; +using static LiteCharms.Features.Extensions.Timezones; -namespace LiteCharms.Infrastructure.Quartz; +namespace LiteCharms.Features.Quartz; public class JobOrchestrator(ISchedulerFactory schedulerFactory) : IJobOrchestrator { diff --git a/LiteCharms.Infrastructure/Quartz/MediatorJob.cs b/LiteCharms.Features/Quartz/MediatorJob.cs similarity index 87% rename from LiteCharms.Infrastructure/Quartz/MediatorJob.cs rename to LiteCharms.Features/Quartz/MediatorJob.cs index 8cf9c75..b3ea928 100644 --- a/LiteCharms.Infrastructure/Quartz/MediatorJob.cs +++ b/LiteCharms.Features/Quartz/MediatorJob.cs @@ -1,6 +1,6 @@ -using LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; -namespace LiteCharms.Infrastructure.Quartz; +namespace LiteCharms.Features.Quartz; [DisallowConcurrentExecution] public class MediatorJob(IMediator mediator) : IJob where TNotification : IEvent diff --git a/LiteCharms.Infrastructure/Quartz/RetryJobListener.cs b/LiteCharms.Features/Quartz/RetryJobListener.cs similarity index 93% rename from LiteCharms.Infrastructure/Quartz/RetryJobListener.cs rename to LiteCharms.Features/Quartz/RetryJobListener.cs index afe84e7..968b8bb 100644 --- a/LiteCharms.Infrastructure/Quartz/RetryJobListener.cs +++ b/LiteCharms.Features/Quartz/RetryJobListener.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Infrastructure.Quartz; +namespace LiteCharms.Features.Quartz; public class RetryJobListener : IJobListener { diff --git a/LiteCharms.Abstractions/EventBusQueueBase.cs b/LiteCharms.Features/ServiceBus/Abstractions/EventBusQueueBase.cs similarity index 73% rename from LiteCharms.Abstractions/EventBusQueueBase.cs rename to LiteCharms.Features/ServiceBus/Abstractions/EventBusQueueBase.cs index 29f7265..31391af 100644 --- a/LiteCharms.Abstractions/EventBusQueueBase.cs +++ b/LiteCharms.Features/ServiceBus/Abstractions/EventBusQueueBase.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Abstractions; public abstract class EventBusQueueBase { diff --git a/LiteCharms.Abstractions/IEventBus.cs b/LiteCharms.Features/ServiceBus/Abstractions/IEventBus.cs similarity index 64% rename from LiteCharms.Abstractions/IEventBus.cs rename to LiteCharms.Features/ServiceBus/Abstractions/IEventBus.cs index f40d372..4f3ebb0 100644 --- a/LiteCharms.Abstractions/IEventBus.cs +++ b/LiteCharms.Features/ServiceBus/Abstractions/IEventBus.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Abstractions; public interface IEventBus { diff --git a/LiteCharms.Abstractions/IEventBusQueue.cs b/LiteCharms.Features/ServiceBus/Abstractions/IEventBusQueue.cs similarity index 56% rename from LiteCharms.Abstractions/IEventBusQueue.cs rename to LiteCharms.Features/ServiceBus/Abstractions/IEventBusQueue.cs index 98750b4..1877555 100644 --- a/LiteCharms.Abstractions/IEventBusQueue.cs +++ b/LiteCharms.Features/ServiceBus/Abstractions/IEventBusQueue.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Abstractions; public interface IEventBusQueue { diff --git a/LiteCharms.Abstractions/Constants.cs b/LiteCharms.Features/ServiceBus/Constants.cs similarity index 55% rename from LiteCharms.Abstractions/Constants.cs rename to LiteCharms.Features/ServiceBus/Constants.cs index ae08f94..ff8d4fe 100644 --- a/LiteCharms.Abstractions/Constants.cs +++ b/LiteCharms.Features/ServiceBus/Constants.cs @@ -1,13 +1,9 @@ -namespace LiteCharms.Abstractions; +namespace LiteCharms.Features.ServiceBus; public static class Constants { public const int QueueBounds = 100000; - public const string ShopSchedulerName = "shop"; - public const string ShopEmailFromName = "Khongisa Shop"; - public const string ShopEmailFromAddress = "shop@litecharms.co.za"; - public const string EmailServiceBus = nameof(EmailServiceBus); public const string GeneralServiceBus = nameof(GeneralServiceBus); public const string SalesServiceBus = nameof(SalesServiceBus); diff --git a/LiteCharms.Infrastructure/ServiceBus/EmailServiceBus.cs b/LiteCharms.Features/ServiceBus/EmailServiceBus.cs similarity index 70% rename from LiteCharms.Infrastructure/ServiceBus/EmailServiceBus.cs rename to LiteCharms.Features/ServiceBus/EmailServiceBus.cs index 5205283..c79438e 100644 --- a/LiteCharms.Infrastructure/ServiceBus/EmailServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/EmailServiceBus.cs @@ -1,7 +1,8 @@ -using LiteCharms.Abstractions; -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.ServiceBus.Abstractions; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Infrastructure.ServiceBus; +namespace LiteCharms.Features.ServiceBus; public class EmailServiceBus(EmailQueue messages) : IEventBus { diff --git a/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs new file mode 100644 index 0000000..5ccae66 --- /dev/null +++ b/LiteCharms.Features/ServiceBus/Exchanges/EmailExchange.cs @@ -0,0 +1,33 @@ +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.ServiceBus.Queues; + +namespace LiteCharms.Features.ServiceBus.Exchanges; + +public class EmailExchange(EmailQueue messages, ILogger logger, IPublisher mediator) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await foreach(IEvent? message in messages.Incoming.ReadAllAsync(stoppingToken)) + { + try + { + switch (message.Name) + { + case "SendShopEmailEnquiryEvent": + await mediator.Publish(message, stoppingToken); + break; + case "ProcessEmailNotificationsEvent": + await mediator.Publish(message, stoppingToken); + break; + default: + logger.LogWarning("Unsupported email event {Event}", message.Name); + break; + } + } + catch (Exception ex) + { + logger.LogError(ex, ex.Message); + } + } + } +} diff --git a/LiteCharms.Infrastructure/ServiceBus/Exchanges/GeneralExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs similarity index 69% rename from LiteCharms.Infrastructure/ServiceBus/Exchanges/GeneralExchange.cs rename to LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs index f21d566..c94fb5d 100644 --- a/LiteCharms.Infrastructure/ServiceBus/Exchanges/GeneralExchange.cs +++ b/LiteCharms.Features/ServiceBus/Exchanges/GeneralExchange.cs @@ -1,6 +1,6 @@ -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Infrastructure.ServiceBus.Exchanges; +namespace LiteCharms.Features.ServiceBus.Exchanges; public class GeneralExchange(GeneralQueue messages) : BackgroundService { diff --git a/LiteCharms.Infrastructure/ServiceBus/Exchanges/SalesExchange.cs b/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs similarity index 69% rename from LiteCharms.Infrastructure/ServiceBus/Exchanges/SalesExchange.cs rename to LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs index 6288734..645ab49 100644 --- a/LiteCharms.Infrastructure/ServiceBus/Exchanges/SalesExchange.cs +++ b/LiteCharms.Features/ServiceBus/Exchanges/SalesExchange.cs @@ -1,6 +1,6 @@ -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Infrastructure.ServiceBus.Exchanges; +namespace LiteCharms.Features.ServiceBus.Exchanges; public class SalesExchange(SalesQueue messages) : BackgroundService { diff --git a/LiteCharms.Infrastructure/ServiceBus/GeneralServiceBus.cs b/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs similarity index 73% rename from LiteCharms.Infrastructure/ServiceBus/GeneralServiceBus.cs rename to LiteCharms.Features/ServiceBus/GeneralServiceBus.cs index 28d6088..94edb37 100644 --- a/LiteCharms.Infrastructure/ServiceBus/GeneralServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/GeneralServiceBus.cs @@ -1,7 +1,8 @@ -using LiteCharms.Abstractions; -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.ServiceBus.Abstractions; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Infrastructure.ServiceBus; +namespace LiteCharms.Features.ServiceBus; public class GeneralServiceBus(GeneralQueue messages) : IEventBus { diff --git a/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs b/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs new file mode 100644 index 0000000..508ad5f --- /dev/null +++ b/LiteCharms.Features/ServiceBus/Queues/EmailQueue.cs @@ -0,0 +1,5 @@ +using LiteCharms.Features.ServiceBus.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Queues; + +public class EmailQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs b/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs new file mode 100644 index 0000000..3d79a2f --- /dev/null +++ b/LiteCharms.Features/ServiceBus/Queues/GeneralQueue.cs @@ -0,0 +1,5 @@ +using LiteCharms.Features.ServiceBus.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Queues; + +public class GeneralQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs b/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs new file mode 100644 index 0000000..8dc5601 --- /dev/null +++ b/LiteCharms.Features/ServiceBus/Queues/SalesQueue.cs @@ -0,0 +1,5 @@ +using LiteCharms.Features.ServiceBus.Abstractions; + +namespace LiteCharms.Features.ServiceBus.Queues; + +public class SalesQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Infrastructure/ServiceBus/SalesServiceBus.cs b/LiteCharms.Features/ServiceBus/SalesServiceBus.cs similarity index 73% rename from LiteCharms.Infrastructure/ServiceBus/SalesServiceBus.cs rename to LiteCharms.Features/ServiceBus/SalesServiceBus.cs index bb3412c..853657b 100644 --- a/LiteCharms.Infrastructure/ServiceBus/SalesServiceBus.cs +++ b/LiteCharms.Features/ServiceBus/SalesServiceBus.cs @@ -1,7 +1,8 @@ -using LiteCharms.Abstractions; -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.Abstractions; +using LiteCharms.Features.ServiceBus.Abstractions; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Infrastructure.ServiceBus; +namespace LiteCharms.Features.ServiceBus; public class SalesServiceBus(SalesQueue messages) : IEventBus { diff --git a/LiteCharms.Features/CartPackages/Commands/AddPackageItemsCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/AddPackageItemsCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs diff --git a/LiteCharms.Features/CartPackages/Commands/CreatePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/CreatePackageCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs diff --git a/LiteCharms.Features/CartPackages/Commands/DeletePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/DeletePackageCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs diff --git a/LiteCharms.Features/CartPackages/Commands/DeletePackageItemsCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/DeletePackageItemsCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs similarity index 97% rename from LiteCharms.Features/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs index ffdd24a..71c54f7 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs similarity index 96% rename from LiteCharms.Features/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs index 4945a73..ff7847e 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs similarity index 96% rename from LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs index 5ec6745..7d7284e 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs similarity index 96% rename from LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs index 8f4e8e7..bad0e89 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs similarity index 96% rename from LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs index 7889c52..15945ff 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs similarity index 95% rename from LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs index 5ef4a91..16c6482 100644 --- a/LiteCharms.Features/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Commands.Handlers; diff --git a/LiteCharms.Features/CartPackages/Commands/UpdatePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/UpdatePackageCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs diff --git a/LiteCharms.Features/CartPackages/Commands/UpdatePackageStatusCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs similarity index 100% rename from LiteCharms.Features/CartPackages/Commands/UpdatePackageStatusCommand.cs rename to LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs diff --git a/LiteCharms.Entities/Package.cs b/LiteCharms.Features/Shop/CartPackages/Entities/Package.cs similarity index 69% rename from LiteCharms.Entities/Package.cs rename to LiteCharms.Features/Shop/CartPackages/Entities/Package.cs index ce8e799..3e4d210 100644 --- a/LiteCharms.Entities/Package.cs +++ b/LiteCharms.Features/Shop/CartPackages/Entities/Package.cs @@ -1,6 +1,4 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.CartPackages.Entities; [EntityTypeConfiguration] public class Package : Models.Package diff --git a/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs b/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs new file mode 100644 index 0000000..e6ff029 --- /dev/null +++ b/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs @@ -0,0 +1,18 @@ +namespace LiteCharms.Features.Shop.CartPackages.Entities; + +public class PackageConfirguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Package)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Name).IsRequired(); + builder.Property(f => f.Summary).IsRequired().HasMaxLength(512); + builder.Property(f => f.Description).IsRequired().HasMaxLength(2048); + builder.Property(f => f.ImageUrl).IsRequired(false).HasMaxLength(2048); + builder.Property(f => f.Active); + } +} diff --git a/LiteCharms.Features/Shop/CartPackages/Entities/PackageItem.cs b/LiteCharms.Features/Shop/CartPackages/Entities/PackageItem.cs new file mode 100644 index 0000000..306975e --- /dev/null +++ b/LiteCharms.Features/Shop/CartPackages/Entities/PackageItem.cs @@ -0,0 +1,11 @@ +using LiteCharms.Features.Shop.Products.Entities; + +namespace LiteCharms.Features.Shop.CartPackages.Entities; + +[EntityTypeConfiguration] +public class PackageItem : Models.PackageItem +{ + public virtual Package? Package { get; set; } + + public virtual ProductPrice? ProductPrice { get; set; } +} diff --git a/LiteCharms.Entities/Configuration/PackageItemConfiguration.cs b/LiteCharms.Features/Shop/CartPackages/Entities/PackageItemConfiguration.cs similarity index 51% rename from LiteCharms.Entities/Configuration/PackageItemConfiguration.cs rename to LiteCharms.Features/Shop/CartPackages/Entities/PackageItemConfiguration.cs index bf31659..c6f1009 100644 --- a/LiteCharms.Entities/Configuration/PackageItemConfiguration.cs +++ b/LiteCharms.Features/Shop/CartPackages/Entities/PackageItemConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.CartPackages.Entities; public class PackageItemConfiguration : IEntityTypeConfiguration { @@ -7,14 +7,21 @@ public class PackageItemConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(PackageItem)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); builder.Property(f => f.PackageId).IsRequired(); builder.Property(f => f.ProductPriceId).IsRequired(); builder.Property(f => f.Active); builder.HasOne(f => f.Package) - .WithMany() - .HasForeignKey(f => f.PackageId) - .OnDelete(DeleteBehavior.NoAction); + .WithMany(f => f.PackageItems) + .HasForeignKey(pi => pi.PackageId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(f => f.ProductPrice) + .WithMany() + .HasForeignKey(pi => pi.ProductPriceId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/LiteCharms.Models/Package.cs b/LiteCharms.Features/Shop/CartPackages/Models/Package.cs similarity index 66% rename from LiteCharms.Models/Package.cs rename to LiteCharms.Features/Shop/CartPackages/Models/Package.cs index cfc0feb..fcc560b 100644 --- a/LiteCharms.Models/Package.cs +++ b/LiteCharms.Features/Shop/CartPackages/Models/Package.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.CartPackages.Models; public class Package { @@ -10,7 +10,11 @@ public class Package public string? Name { get; set; } + public string? Summary { get; set; } + public string? Description { get; set; } + public string? ImageUrl { get; set; } + public bool Active { get; set; } } diff --git a/LiteCharms.Models/PackageItem.cs b/LiteCharms.Features/Shop/CartPackages/Models/PackageItem.cs similarity index 80% rename from LiteCharms.Models/PackageItem.cs rename to LiteCharms.Features/Shop/CartPackages/Models/PackageItem.cs index 2c27ad3..ff81c0a 100644 --- a/LiteCharms.Models/PackageItem.cs +++ b/LiteCharms.Features/Shop/CartPackages/Models/PackageItem.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.CartPackages.Models; public class PackageItem { diff --git a/LiteCharms.Features/CartPackages/Queries/GetPackageItemsQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs similarity index 89% rename from LiteCharms.Features/CartPackages/Queries/GetPackageItemsQuery.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs index e0830af..3315726 100644 --- a/LiteCharms.Features/CartPackages/Queries/GetPackageItemsQuery.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; namespace LiteCharms.Features.CartPackages.Queries; diff --git a/LiteCharms.Features/CartPackages/Queries/GetPackageQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs similarity index 89% rename from LiteCharms.Features/CartPackages/Queries/GetPackageQuery.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs index 1384783..a05264e 100644 --- a/LiteCharms.Features/CartPackages/Queries/GetPackageQuery.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; namespace LiteCharms.Features.CartPackages.Queries; diff --git a/LiteCharms.Features/CartPackages/Queries/GetPackagesQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs similarity index 94% rename from LiteCharms.Features/CartPackages/Queries/GetPackagesQuery.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs index 351a141..036ff09 100644 --- a/LiteCharms.Features/CartPackages/Queries/GetPackagesQuery.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; namespace LiteCharms.Features.CartPackages.Queries; diff --git a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs index 487b203..3b19cdc 100644 --- a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Queries.Handlers; diff --git a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs similarity index 90% rename from LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs index 7a01fb5..d822584 100644 --- a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Queries.Handlers; diff --git a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs similarity index 93% rename from LiteCharms.Features/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs rename to LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs index b495bb0..1ef691f 100644 --- a/LiteCharms.Features/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs +++ b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.CartPackages.Queries.Handlers; diff --git a/LiteCharms.Features/Customers/Commands/CreateCustomerCommand.cs b/LiteCharms.Features/Shop/Customers/Commands/CreateCustomerCommand.cs similarity index 100% rename from LiteCharms.Features/Customers/Commands/CreateCustomerCommand.cs rename to LiteCharms.Features/Shop/Customers/Commands/CreateCustomerCommand.cs diff --git a/LiteCharms.Features/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs similarity index 97% rename from LiteCharms.Features/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs rename to LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs index 82f836b..bf1a5d6 100644 --- a/LiteCharms.Features/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs +++ b/LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Customers.Commands.Handlers; diff --git a/LiteCharms.Features/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs similarity index 97% rename from LiteCharms.Features/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs rename to LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs index 72b9109..031f591 100644 --- a/LiteCharms.Features/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs +++ b/LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Customers.Commands.Handlers; diff --git a/LiteCharms.Features/Customers/Commands/UpdateCustomerCommand.cs b/LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs similarity index 100% rename from LiteCharms.Features/Customers/Commands/UpdateCustomerCommand.cs rename to LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs diff --git a/LiteCharms.Entities/Customer.cs b/LiteCharms.Features/Shop/Customers/Entities/Customer.cs similarity index 58% rename from LiteCharms.Entities/Customer.cs rename to LiteCharms.Features/Shop/Customers/Entities/Customer.cs index bd3bf40..7b78bc8 100644 --- a/LiteCharms.Entities/Customer.cs +++ b/LiteCharms.Features/Shop/Customers/Entities/Customer.cs @@ -1,6 +1,9 @@ -using LiteCharms.Entities.Configuration; +using LiteCharms.Features.Shop.Leads.Entities; +using LiteCharms.Features.Shop.Orders.Entities; +using LiteCharms.Features.Shop.Quotes.Entities; +using LiteCharms.Features.Shop.ShoppingCarts.Entities; -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Customers.Entities; [EntityTypeConfiguration] public class Customer : Models.Customer diff --git a/LiteCharms.Entities/Configuration/CustomerConfiguration.cs b/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs similarity index 77% rename from LiteCharms.Entities/Configuration/CustomerConfiguration.cs rename to LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs index 9792251..335c1b0 100644 --- a/LiteCharms.Entities/Configuration/CustomerConfiguration.cs +++ b/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.Customers.Entities; public class CustomerConfiguration : IEntityTypeConfiguration { @@ -7,8 +7,8 @@ public class CustomerConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Customer)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd(); - builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); + builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); builder.Property(f => f.Company); builder.Property(f => f.Name).IsRequired(); builder.Property(f => f.LastName).IsRequired(); @@ -26,10 +26,5 @@ public class CustomerConfiguration : IEntityTypeConfiguration builder.Property(f => f.Country); builder.Property(f => f.PostalCode); builder.Property(f => f.Active).HasDefaultValue(true); - - builder.HasMany(f => f.Leads) - .WithOne(f => f.Customer) - .HasForeignKey(f => f.CustomerId) - .OnDelete(DeleteBehavior.NoAction); } } \ No newline at end of file diff --git a/LiteCharms.Models/Customer.cs b/LiteCharms.Features/Shop/Customers/Models/Customer.cs similarity index 93% rename from LiteCharms.Models/Customer.cs rename to LiteCharms.Features/Shop/Customers/Models/Customer.cs index b322f8a..14387f6 100644 --- a/LiteCharms.Models/Customer.cs +++ b/LiteCharms.Features/Shop/Customers/Models/Customer.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Customers.Models; public class Customer { diff --git a/LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs b/LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs similarity index 89% rename from LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs rename to LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs index 5ea7394..c6ee4b0 100644 --- a/LiteCharms.Features/Customers/Queries/GetCustomerQuery.cs +++ b/LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Customers.Models; namespace LiteCharms.Features.Customers.Queries; diff --git a/LiteCharms.Features/Customers/Queries/GetCustomersQuery.cs b/LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs similarity index 93% rename from LiteCharms.Features/Customers/Queries/GetCustomersQuery.cs rename to LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs index 3f271b3..9c830b4 100644 --- a/LiteCharms.Features/Customers/Queries/GetCustomersQuery.cs +++ b/LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Customers.Models; namespace LiteCharms.Features.Customers.Queries; diff --git a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs similarity index 90% rename from LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs rename to LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs index 0f921db..cbe478d 100644 --- a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomerQueryHandler.cs +++ b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Customers.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Customers.Queries.Handlers; diff --git a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomersQueryHandler.cs b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Customers/Queries/Handlers/GetCustomersQueryHandler.cs rename to LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs index 960ef3a..b9d1e99 100644 --- a/LiteCharms.Features/Customers/Queries/Handlers/GetCustomersQueryHandler.cs +++ b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Customers.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Customers.Queries.Handlers; diff --git a/LiteCharms.Models/Enums.cs b/LiteCharms.Features/Shop/Enums.cs similarity index 76% rename from LiteCharms.Models/Enums.cs rename to LiteCharms.Features/Shop/Enums.cs index b672e25..6f25546 100644 --- a/LiteCharms.Models/Enums.cs +++ b/LiteCharms.Features/Shop/Enums.cs @@ -1,4 +1,16 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop; + +public enum EmailStatuses : int +{ + GeneralError = 0, + AuthenticationError = 1, + ProtocolError = 2, + Connected = 3, + Disconnected = 4, + TooManyConnections = 5, + ConnectionAborted = 6, + Success = 7 +} public enum CorrelationIdTypes : int { @@ -17,7 +29,7 @@ public enum CorrelationIdTypes : int public enum Priorities : int { - Low = 0, + Low = 0, Medium = 1, High = 2, } @@ -66,3 +78,4 @@ public enum NotificationDirection : int Outgoing = 1, Neutral = 2 } + diff --git a/LiteCharms.Features/Leads/Commands/CreateLeadCommand.cs b/LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs similarity index 100% rename from LiteCharms.Features/Leads/Commands/CreateLeadCommand.cs rename to LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs diff --git a/LiteCharms.Features/Leads/Commands/Handlers/CreateLeadCommandHandler.cs b/LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs similarity index 95% rename from LiteCharms.Features/Leads/Commands/Handlers/CreateLeadCommandHandler.cs rename to LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs index 256bc22..a51d31a 100644 --- a/LiteCharms.Features/Leads/Commands/Handlers/CreateLeadCommandHandler.cs +++ b/LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs @@ -1,5 +1,5 @@ -using LiteCharms.Features.Utilities.Hash.Commands; -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Utilities.Hash.Commands; namespace LiteCharms.Features.Leads.Commands.Handlers; diff --git a/LiteCharms.Features/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs b/LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs similarity index 95% rename from LiteCharms.Features/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs rename to LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs index afaa15c..67f14fc 100644 --- a/LiteCharms.Features/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs +++ b/LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Leads.Commands.Handlers; diff --git a/LiteCharms.Features/Leads/Commands/UpdateLeadCommand.cs b/LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs similarity index 92% rename from LiteCharms.Features/Leads/Commands/UpdateLeadCommand.cs rename to LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs index a201b70..ef31b01 100644 --- a/LiteCharms.Features/Leads/Commands/UpdateLeadCommand.cs +++ b/LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs @@ -1,4 +1,5 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop; +using LiteCharms.Models; namespace LiteCharms.Features.Leads.Commands; diff --git a/LiteCharms.Entities/Lead.cs b/LiteCharms.Features/Shop/Leads/Entities/Lead.cs similarity index 57% rename from LiteCharms.Entities/Lead.cs rename to LiteCharms.Features/Shop/Leads/Entities/Lead.cs index b1bdf62..deda2d6 100644 --- a/LiteCharms.Entities/Lead.cs +++ b/LiteCharms.Features/Shop/Leads/Entities/Lead.cs @@ -1,6 +1,6 @@ -using LiteCharms.Entities.Configuration; +using LiteCharms.Features.Shop.Customers.Models; -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Leads.Entities; [EntityTypeConfiguration] public class Lead : Models.Lead diff --git a/LiteCharms.Entities/Configuration/LeadConfiguration.cs b/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs similarity index 86% rename from LiteCharms.Entities/Configuration/LeadConfiguration.cs rename to LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs index 8c8d004..482677c 100644 --- a/LiteCharms.Entities/Configuration/LeadConfiguration.cs +++ b/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.Leads.Entities; public class LeadConfiguration : IEntityTypeConfiguration { @@ -7,8 +7,8 @@ public class LeadConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Lead)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd(); - builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); + builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); builder.Property(f => f.CustomerId).IsRequired(false); builder.Property(f => f.Source); builder.Property(f => f.ClickId); diff --git a/LiteCharms.Models/Lead.cs b/LiteCharms.Features/Shop/Leads/Models/Lead.cs similarity index 93% rename from LiteCharms.Models/Lead.cs rename to LiteCharms.Features/Shop/Leads/Models/Lead.cs index 5222114..0cb9fa1 100644 --- a/LiteCharms.Models/Lead.cs +++ b/LiteCharms.Features/Shop/Leads/Models/Lead.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Leads.Models; public class Lead { diff --git a/LiteCharms.Features/Leads/Queries/GetCustomerLeadsQuery.cs b/LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs similarity index 94% rename from LiteCharms.Features/Leads/Queries/GetCustomerLeadsQuery.cs rename to LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs index ddede3c..de53634 100644 --- a/LiteCharms.Features/Leads/Queries/GetCustomerLeadsQuery.cs +++ b/LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Leads.Models; namespace LiteCharms.Features.Leads.Queries; diff --git a/LiteCharms.Features/Leads/Queries/GetLeadsQuery.cs b/LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs similarity index 93% rename from LiteCharms.Features/Leads/Queries/GetLeadsQuery.cs rename to LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs index 3278bf7..d280e65 100644 --- a/LiteCharms.Features/Leads/Queries/GetLeadsQuery.cs +++ b/LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Leads.Models; namespace LiteCharms.Features.Leads.Queries; diff --git a/LiteCharms.Features/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs rename to LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs index ed90212..dd9ba15 100644 --- a/LiteCharms.Features/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs +++ b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Leads.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Leads.Queries.Handlers; diff --git a/LiteCharms.Features/Leads/Queries/Handlers/GetLeadsQueryHandler.cs b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Leads/Queries/Handlers/GetLeadsQueryHandler.cs rename to LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs index 0ebaafb..417ff5c 100644 --- a/LiteCharms.Features/Leads/Queries/Handlers/GetLeadsQueryHandler.cs +++ b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Leads.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Leads.Queries.Handlers; diff --git a/LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs b/LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs similarity index 73% rename from LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs rename to LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs index 2b3e933..59e69d6 100644 --- a/LiteCharms.Features/Notifications/Commands/CreateNotificationCommand.cs +++ b/LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs @@ -1,8 +1,9 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop; +using LiteCharms.Models; namespace LiteCharms.Features.Notifications.Commands; -public class CreateNotificationCommand : IRequest> +public class CreateNotification : IRequest> { public NotificationDirection Direction { get; set; } @@ -30,7 +31,7 @@ public class CreateNotificationCommand : IRequest> public bool IsHtml { get; set; } - private CreateNotificationCommand(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) + private CreateNotification(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) { Direction = direction; Sender = sender; @@ -47,7 +48,7 @@ public class CreateNotificationCommand : IRequest> IsHtml = isHtml; } - public static CreateNotificationCommand Create(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) + public static CreateNotification Create(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) { if (string.IsNullOrWhiteSpace(sender)) throw new ArgumentException("Sender name is required.", nameof(sender)); diff --git a/LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs similarity index 92% rename from LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs rename to LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs index e7a3fb4..facd9a3 100644 --- a/LiteCharms.Features/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs @@ -1,10 +1,10 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Notifications.Commands.Handlers; -public class CreateNotificationCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> +public class CreateNotificationCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> { - public async ValueTask> Handle(CreateNotificationCommand request, CancellationToken cancellationToken) + public async ValueTask> Handle(CreateNotification request, CancellationToken cancellationToken) { try { diff --git a/LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs similarity index 96% rename from LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs rename to LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs index 5ce4e81..6d32acc 100644 --- a/LiteCharms.Features/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Notifications.Commands.Handlers; diff --git a/LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs b/LiteCharms.Features/Shop/Notifications/Commands/UpdateNotificationCommand.cs similarity index 100% rename from LiteCharms.Features/Notifications/Commands/UpdateNotificationCommand.cs rename to LiteCharms.Features/Shop/Notifications/Commands/UpdateNotificationCommand.cs diff --git a/LiteCharms.Entities/Notification.cs b/LiteCharms.Features/Shop/Notifications/Entities/Notification.cs similarity index 60% rename from LiteCharms.Entities/Notification.cs rename to LiteCharms.Features/Shop/Notifications/Entities/Notification.cs index 2f3bf11..36a7dbc 100644 --- a/LiteCharms.Entities/Notification.cs +++ b/LiteCharms.Features/Shop/Notifications/Entities/Notification.cs @@ -1,6 +1,4 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Notifications.Entities; [EntityTypeConfiguration] public class Notification : Models.Notification; diff --git a/LiteCharms.Entities/Configuration/NotificationConfiguration.cs b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs similarity index 88% rename from LiteCharms.Entities/Configuration/NotificationConfiguration.cs rename to LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs index 45319a4..37b7659 100644 --- a/LiteCharms.Entities/Configuration/NotificationConfiguration.cs +++ b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs @@ -1,6 +1,4 @@ -using LiteCharms.Models; - -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.Notifications.Entities; public class NotificationConfiguration : IEntityTypeConfiguration { @@ -9,8 +7,8 @@ public class NotificationConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(Notification)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); - builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); builder.Property(f => f.Direction).IsRequired().HasConversion(); builder.Property(f => f.Platform).IsRequired().HasConversion(); builder.Property(f => f.Priority).IsRequired().HasConversion(); diff --git a/LiteCharms.Features/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs b/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs similarity index 89% rename from LiteCharms.Features/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs rename to LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs index 61b24d5..332ec86 100644 --- a/LiteCharms.Features/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs @@ -1,6 +1,7 @@ using LiteCharms.Features.Email.Commands; -using LiteCharms.Infrastructure.Database; -using static LiteCharms.Abstractions.Constants; +using LiteCharms.Features.Shop.Notifications.Entities; +using LiteCharms.Features.Shop.Postgres; +using static LiteCharms.Features.ServiceBus.Constants; namespace LiteCharms.Features.Notifications.Events.Handlers; @@ -49,7 +50,7 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory SendEmailAsync(Entities.Notification notification, CancellationToken cancellationToken = default) + private async Task SendEmailAsync(Notification notification, CancellationToken cancellationToken = default) { try { diff --git a/LiteCharms.Features/Notifications/Events/ProcessEmailNotificationsEvent.cs b/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs similarity index 91% rename from LiteCharms.Features/Notifications/Events/ProcessEmailNotificationsEvent.cs rename to LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs index 9ac754f..bbc4da4 100644 --- a/LiteCharms.Features/Notifications/Events/ProcessEmailNotificationsEvent.cs +++ b/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs @@ -1,4 +1,4 @@ -using LiteCharms.Abstractions; +using LiteCharms.Features.Abstractions; namespace LiteCharms.Features.Notifications.Events; diff --git a/LiteCharms.Features/Shop/Notifications/INotificationService.cs b/LiteCharms.Features/Shop/Notifications/INotificationService.cs new file mode 100644 index 0000000..d036de3 --- /dev/null +++ b/LiteCharms.Features/Shop/Notifications/INotificationService.cs @@ -0,0 +1,9 @@ +using LiteCharms.Features.Shop.Notifications.Models; + +namespace LiteCharms.Features.Shop.Notifications; + +public interface INotificationService +{ + Task> CreateNotificationAsync(CreateNotification request, CancellationToken cancellationToken = default); + Task UpdateNotificationAsync(UpdateNotification request, CancellationToken cancellationToken = default); +} diff --git a/LiteCharms.Models/Notification.cs b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs similarity index 93% rename from LiteCharms.Models/Notification.cs rename to LiteCharms.Features/Shop/Notifications/Models/Notification.cs index 6108cbb..cba815c 100644 --- a/LiteCharms.Models/Notification.cs +++ b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Notifications.Models; public class Notification { diff --git a/LiteCharms.Features/Shop/Notifications/Models/Records.cs b/LiteCharms.Features/Shop/Notifications/Models/Records.cs new file mode 100644 index 0000000..2f0f949 --- /dev/null +++ b/LiteCharms.Features/Shop/Notifications/Models/Records.cs @@ -0,0 +1,41 @@ +namespace LiteCharms.Features.Shop.Notifications.Models; + +public record CreateNotification +{ + public NotificationDirection Direction { get; set; } + + public string? Sender { get; set; } + + public string? SenderAddress { get; set; } + + public string? Subject { get; set; } + + public string? Message { get; set; } + + public NotificationPlatforms Platform { get; set; } + + public Priorities Priority { get; set; } + + public string? Recipient { get; set; } + + public string? RecipientAddress { get; set; } + + public string? CorrelationId { get; set; } + + public CorrelationIdTypes CorrelationIdType { get; set; } + + public bool IsInternal { get; set; } + + public bool IsHtml { get; set; } +} + +public class UpdateNotification +{ + public Guid NotificationId { get; set; } + + public bool Processed { get; set; } + + public bool HasError { get; set; } + + public string[]? Errors { get; set; } +} diff --git a/LiteCharms.Features/Shop/Notifications/NotificationService.cs b/LiteCharms.Features/Shop/Notifications/NotificationService.cs new file mode 100644 index 0000000..02ee707 --- /dev/null +++ b/LiteCharms.Features/Shop/Notifications/NotificationService.cs @@ -0,0 +1,5 @@ +namespace LiteCharms.Features.Shop.Notifications; + +public class NotificationService : INotificationService +{ +} diff --git a/LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs similarity index 90% rename from LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs rename to LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs index f41aea5..6c599fb 100644 --- a/LiteCharms.Features/Notifications/Queries/GetNotificationQuery.cs +++ b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Notifications.Models; namespace LiteCharms.Features.Notifications.Queries; diff --git a/LiteCharms.Features/Notifications/Queries/GetNotificationsQuery.cs b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs similarity index 93% rename from LiteCharms.Features/Notifications/Queries/GetNotificationsQuery.cs rename to LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs index 6eef589..6a10a4e 100644 --- a/LiteCharms.Features/Notifications/Queries/GetNotificationsQuery.cs +++ b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Notifications.Models; namespace LiteCharms.Features.Notifications.Queries; diff --git a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs similarity index 91% rename from LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs rename to LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs index 5eac5d8..11d5553 100644 --- a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Notifications.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Notifications.Queries.Handlers; diff --git a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs rename to LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs index 70e448f..17e6094 100644 --- a/LiteCharms.Features/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Notifications.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Notifications.Queries.Handlers; diff --git a/LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs b/LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs similarity index 100% rename from LiteCharms.Features/Orders/Commands/CreateOrderCommand.cs rename to LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs diff --git a/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs similarity index 96% rename from LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs rename to LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs index b97f83e..95a599a 100644 --- a/LiteCharms.Features/Orders/Commands/Handlers/CreateOrderCommandHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs @@ -1,4 +1,5 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop; +using LiteCharms.Features.Shop.Postgres; using LiteCharms.Models; namespace LiteCharms.Features.Orders.Commands.Handlers; diff --git a/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs similarity index 95% rename from LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs rename to LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs index 0cfe348..2c0052f 100644 --- a/LiteCharms.Features/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Orders.Commands.Handlers; diff --git a/LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs b/LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs similarity index 93% rename from LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs rename to LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs index 3e6d1c6..eefc734 100644 --- a/LiteCharms.Features/Orders/Commands/UpdateOrderStatusCommand.cs +++ b/LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs @@ -1,4 +1,5 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop; +using LiteCharms.Models; namespace LiteCharms.Features.Orders.Commands; diff --git a/LiteCharms.Features/Shop/Orders/Entities/Order.cs b/LiteCharms.Features/Shop/Orders/Entities/Order.cs new file mode 100644 index 0000000..f5dd1d0 --- /dev/null +++ b/LiteCharms.Features/Shop/Orders/Entities/Order.cs @@ -0,0 +1,17 @@ +using LiteCharms.Features.Shop.Customers.Entities; +using LiteCharms.Features.Shop.Quotes.Entities; +using LiteCharms.Features.Shop.ShoppingCarts.Entities; + +namespace LiteCharms.Features.Shop.Orders.Entities; + +[EntityTypeConfiguration] +public class Order : Models.Order +{ + public virtual ICollection? Refunds { get; set; } + + public virtual Customer? Customer { get; set; } + + public virtual Quote? Quote { get; set; } + + public virtual ShoppingCart? ShoppingCart { get; set; } +} diff --git a/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs new file mode 100644 index 0000000..d79fb0e --- /dev/null +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs @@ -0,0 +1,25 @@ +namespace LiteCharms.Features.Shop.Orders.Entities; + +public class OrderConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Order)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.CustomerId).IsRequired(); + builder.Property(f => f.Status).HasConversion().IsRequired(); + builder.Property(f => f.Requirements).HasColumnType("jsonb").IsRequired(false); + builder.Property(f => f.Notes).HasColumnType("jsonb").IsRequired(false); + builder.Property(f => f.Terms).HasColumnType("jsonb").IsRequired(false); + builder.Property(f => f.InvoiceUrl).IsRequired(false).HasMaxLength(2048); + + builder.HasOne(o => o.Customer) + .WithMany(c => c.Orders) + .HasForeignKey(o => o.CustomerId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/LiteCharms.Entities/OrderRefund.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs similarity index 68% rename from LiteCharms.Entities/OrderRefund.cs rename to LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs index e5a285d..33591d7 100644 --- a/LiteCharms.Entities/OrderRefund.cs +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs @@ -1,6 +1,4 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Orders.Entities; [EntityTypeConfiguration] public class OrderRefund : Models.OrderRefund diff --git a/LiteCharms.Entities/Configuration/OrderRefundConfiguration.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs similarity index 63% rename from LiteCharms.Entities/Configuration/OrderRefundConfiguration.cs rename to LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs index 5550805..f353123 100644 --- a/LiteCharms.Entities/Configuration/OrderRefundConfiguration.cs +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.Orders.Entities; public class OrderRefundConfiguration : IEntityTypeConfiguration { @@ -7,13 +7,15 @@ public class OrderRefundConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(OrderRefund)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd(); + builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); builder.Property(f => f.OrderId).IsRequired(); builder.Property(f => f.Reason).IsRequired(); builder.Property(f => f.Amount).IsRequired().HasPrecision(18, 2); - builder.HasOne(f => f.Order) - .WithOne(o => o.Refund) - .HasForeignKey(o => o.OrderId); + builder.HasOne(r => r.Order) + .WithMany(o => o.Refunds) + .HasForeignKey(r => r.OrderId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); } } diff --git a/LiteCharms.Models/Order.cs b/LiteCharms.Features/Shop/Orders/Models/Order.cs similarity index 64% rename from LiteCharms.Models/Order.cs rename to LiteCharms.Features/Shop/Orders/Models/Order.cs index 3c55c81..d093993 100644 --- a/LiteCharms.Models/Order.cs +++ b/LiteCharms.Features/Shop/Orders/Models/Order.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Orders.Models; public class Order { @@ -10,12 +10,6 @@ public class Order public Guid CustomerId { get; set; } - public Guid? QuoteId { get; set; } - - public Guid ShoppingCartId { get; set; } - - public Guid? RefundId { get; set; } - public OrderStatus Status { get; set; } public string[]? Requirements { get; set; } @@ -24,5 +18,5 @@ public class Order public string[]? Terms { get; set; } - public bool DepositRequired { get; set; } + public string? InvoiceUrl { get; set; } } diff --git a/LiteCharms.Models/OrderRefund.cs b/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs similarity index 81% rename from LiteCharms.Models/OrderRefund.cs rename to LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs index 292e918..34668a7 100644 --- a/LiteCharms.Models/OrderRefund.cs +++ b/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Orders.Models; public class OrderRefund { diff --git a/LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs similarity index 90% rename from LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs rename to LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs index 7d11f91..f74f5c4 100644 --- a/LiteCharms.Features/Orders/Queries/GetCustomerOrdersQuery.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; namespace LiteCharms.Features.Orders.Queries; diff --git a/LiteCharms.Features/Orders/Queries/GetOrderRefundQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs similarity index 93% rename from LiteCharms.Features/Orders/Queries/GetOrderRefundQuery.cs rename to LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs index 887e03b..01295cf 100644 --- a/LiteCharms.Features/Orders/Queries/GetOrderRefundQuery.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; namespace LiteCharms.Features.Orders.Queries; diff --git a/LiteCharms.Features/Orders/Queries/GetOrdersQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs similarity index 93% rename from LiteCharms.Features/Orders/Queries/GetOrdersQuery.cs rename to LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs index c0c2c3b..0fc45d8 100644 --- a/LiteCharms.Features/Orders/Queries/GetOrdersQuery.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; namespace LiteCharms.Features.Orders.Queries; diff --git a/LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs rename to LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs index eb9e79f..a2f65d9 100644 --- a/LiteCharms.Features/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Orders.Queries.Handlers; diff --git a/LiteCharms.Features/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs similarity index 92% rename from LiteCharms.Features/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs rename to LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs index 47dd481..3493b11 100644 --- a/LiteCharms.Features/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Orders.Queries.Handlers; diff --git a/LiteCharms.Features/Orders/Queries/Handlers/GetOrdersQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Orders/Queries/Handlers/GetOrdersQueryHandler.cs rename to LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs index f244869..898f3fc 100644 --- a/LiteCharms.Features/Orders/Queries/Handlers/GetOrdersQueryHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Orders.Queries.Handlers; diff --git a/LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs similarity index 90% rename from LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs index c4021f7..9013f10 100644 --- a/LiteCharms.Features/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs @@ -1,6 +1,7 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Orders.Refunds.Commands; +using LiteCharms.Features.Shop.Postgres; -namespace LiteCharms.Features.Refunds.Commands.Handlers; +namespace LiteCharms.Features.Shop.Orders.Refunds.Commands.Handlers; public class RefundCustomerCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> { diff --git a/LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs similarity index 85% rename from LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs index 9f6fb00..ac88a49 100644 --- a/LiteCharms.Features/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs @@ -1,6 +1,7 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Orders.Refunds.Commands; +using LiteCharms.Features.Shop.Postgres; -namespace LiteCharms.Features.Refunds.Commands.Handlers; +namespace LiteCharms.Features.Shop.Orders.Refunds.Commands.Handlers; public class UpdateOrderRefundCommandHandler(IDbContextFactory contextFactory) : IRequestHandler { diff --git a/LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs similarity index 94% rename from LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs index ccfda6e..ac5c75b 100644 --- a/LiteCharms.Features/Refunds/Commands/RefundCustomerCommand.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Features.Refunds.Commands; +namespace LiteCharms.Features.Shop.Orders.Refunds.Commands; public class RefundCustomerCommand : IRequest> { diff --git a/LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs similarity index 92% rename from LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs index bd9ab59..9f69b2f 100644 --- a/LiteCharms.Features/Refunds/Commands/UpdateOrderRefundCommand.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Features.Refunds.Commands; +namespace LiteCharms.Features.Shop.Orders.Refunds.Commands; public class UpdateOrderRefundCommand : IRequest { diff --git a/LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs similarity index 80% rename from LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs index 24d2cc9..56d19f6 100644 --- a/LiteCharms.Features/Refunds/Queries/GetCustomerRefundsQuery.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs @@ -1,6 +1,6 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; -namespace LiteCharms.Features.Refunds.Queries; +namespace LiteCharms.Features.Shop.Orders.Refunds.Queries; public class GetCustomerRefundsQuery : IRequest> { diff --git a/LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs similarity index 80% rename from LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs index 9f4d375..ddeaa7b 100644 --- a/LiteCharms.Features/Refunds/Queries/GetRefundQuery.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs @@ -1,6 +1,6 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; -namespace LiteCharms.Features.Refunds.Queries; +namespace LiteCharms.Features.Shop.Orders.Refunds.Queries; public class GetRefundQuery : IRequest> { diff --git a/LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs similarity index 85% rename from LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs index c198b76..8439f88 100644 --- a/LiteCharms.Features/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs @@ -1,9 +1,9 @@ using LiteCharms.Extensions; -using LiteCharms.Features.Refunds.Queries; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Orders.Refunds.Queries; +using LiteCharms.Features.Shop.Postgres; -namespace LiteCharms.Features.Refunds.Queries.Handlers; +namespace LiteCharms.Features.Shop.Orders.Refunds.Queries.Handlers; public class GetCustomerRefundsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> { diff --git a/LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs similarity index 80% rename from LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs rename to LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs index 2561031..a363945 100644 --- a/LiteCharms.Features/Refunds/Queries/Handlers/GetRefundQueryHandler.cs +++ b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs @@ -1,8 +1,9 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Orders.Refunds.Queries; +using LiteCharms.Features.Shop.Postgres; -namespace LiteCharms.Features.Refunds.Queries.Handlers; +namespace LiteCharms.Features.Shop.Orders.Refunds.Queries.Handlers; public class GetRefundQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> { diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.Designer.cs b/LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.Designer.cs similarity index 86% rename from LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.Designer.cs rename to LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.Designer.cs index 4624007..5900074 100644 --- a/LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.Designer.cs +++ b/LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.Designer.cs @@ -1,6 +1,6 @@ // using System; -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; @@ -9,10 +9,10 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace LiteCharms.Infrastructure.Database.Migrations +namespace LiteCharms.Features.Shop.Postgres.Migrations { [DbContext(typeof(ShopDbContext))] - [Migration("20260510132008_Init")] + [Migration("20260512065421_Init")] partial class Init { /// @@ -50,7 +50,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Discord") .HasColumnType("text"); @@ -86,7 +87,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("text"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.Property("Website") @@ -130,7 +130,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); @@ -148,7 +149,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("bigint"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.Property("WebClickId") @@ -176,7 +176,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Direction") .HasColumnType("integer"); @@ -234,7 +235,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("text"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -250,29 +250,22 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); - b.Property("DepositRequired") - .HasColumnType("boolean"); + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.PrimitiveCollection("Notes") .HasColumnType("jsonb"); - b.Property("QuoteId") - .HasColumnType("uuid"); - - b.Property("RefundId") - .HasColumnType("uuid"); - b.PrimitiveCollection("Requirements") .HasColumnType("jsonb"); - b.Property("ShoppingCartId") - .HasColumnType("uuid"); - b.Property("Status") .HasColumnType("integer"); @@ -280,19 +273,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("jsonb"); 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); }); @@ -308,7 +294,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("OrderId") .HasColumnType("uuid"); @@ -319,8 +306,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasKey("Id"); - b.HasIndex("OrderId") - .IsUnique(); + b.HasIndex("OrderId"); b.ToTable("OrderRefund", (string)null); }); @@ -336,27 +322,32 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Description") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("ShoppingCartId") - .HasColumnType("uuid"); + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); - b.HasIndex("ShoppingCartId"); - b.ToTable("Package", (string)null); }); @@ -371,14 +362,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("PackageId") .HasColumnType("uuid"); - b.Property("PackageId1") - .HasColumnType("uuid"); - b.Property("ProductPriceId") .HasColumnType("uuid"); @@ -386,7 +375,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasIndex("PackageId"); - b.HasIndex("PackageId1"); + b.HasIndex("ProductPriceId"); b.ToTable("PackageItem", (string)null); }); @@ -404,12 +393,25 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("Description") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.Property("Name") .IsRequired() .HasColumnType("text"); + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.PrimitiveCollection("Thumbnails") + .HasColumnType("jsonb"); + b.HasKey("Id"); b.ToTable("Product", (string)null); @@ -426,7 +428,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Discount") .HasPrecision(18, 2) @@ -440,7 +443,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("uuid"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -458,35 +460,40 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); - b.Property("CustomerId1") - .HasColumnType("uuid"); - b.Property("ExpiredAt") .HasColumnType("timestamp with time zone"); + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("OrderId") + .HasColumnType("uuid"); + b.Property("Reason") .HasColumnType("text"); - b.Property("ShoppingCartId") + 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("CustomerId1"); + b.HasIndex("OrderId") + .IsUnique(); b.HasIndex("ShoppingCartId") .IsUnique(); @@ -502,25 +509,25 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("CustomerId") + b.Property("CustomerId") .HasColumnType("uuid"); b.Property("OrderId") .HasColumnType("uuid"); - b.Property("QuoteId") - .HasColumnType("uuid"); - b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); b.HasIndex("CustomerId"); + b.HasIndex("OrderId") + .IsUnique(); + b.ToTable("ShoppingCart", (string)null); }); @@ -562,7 +569,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("PackageId") .HasColumnType("uuid"); @@ -583,8 +591,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations { b.HasOne("LiteCharms.Entities.Customer", "Customer") .WithMany("Leads") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.NoAction); + .HasForeignKey("CustomerId"); b.Navigation("Customer"); }); @@ -597,55 +604,37 @@ namespace LiteCharms.Infrastructure.Database.Migrations .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") + .WithMany("Refunds") + .HasForeignKey("OrderId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Order"); }); - modelBuilder.Entity("LiteCharms.Entities.Package", b => - { - b.HasOne("LiteCharms.Entities.ShoppingCart", null) - .WithMany("Packages") - .HasForeignKey("ShoppingCartId"); - }); - modelBuilder.Entity("LiteCharms.Entities.PackageItem", b => { b.HasOne("LiteCharms.Entities.Package", "Package") - .WithMany() + .WithMany("PackageItems") .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Package", null) - .WithMany("PackageItems") - .HasForeignKey("PackageId1"); + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); b.Navigation("Package"); + + b.Navigation("ProductPrice"); }); modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => @@ -662,23 +651,23 @@ namespace LiteCharms.Infrastructure.Database.Migrations modelBuilder.Entity("LiteCharms.Entities.Quote", b => { b.HasOne("LiteCharms.Entities.Customer", "Customer") - .WithMany() + .WithMany("Quotes") .HasForeignKey("CustomerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Customer", null) - .WithMany("Quotes") - .HasForeignKey("CustomerId1"); + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Entities.Quote", "OrderId"); b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") .WithOne("Quote") - .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); + .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId"); b.Navigation("Customer"); + b.Navigation("Order"); + b.Navigation("ShoppingCart"); }); @@ -687,9 +676,17 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasOne("LiteCharms.Entities.Customer", "Customer") .WithMany("ShoppingCarts") .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.NoAction); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("ShoppingCart") + .HasForeignKey("LiteCharms.Entities.ShoppingCart", "OrderId") + .OnDelete(DeleteBehavior.SetNull); b.Navigation("Customer"); + + b.Navigation("Order"); }); modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => @@ -716,13 +713,13 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasOne("LiteCharms.Entities.Package", "Package") .WithMany() .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") - .WithMany() + .WithMany("ShoppingCartPackages") .HasForeignKey("ShoppingCartId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Package"); @@ -743,7 +740,11 @@ namespace LiteCharms.Infrastructure.Database.Migrations modelBuilder.Entity("LiteCharms.Entities.Order", b => { - b.Navigation("Refund"); + b.Navigation("Quote"); + + b.Navigation("Refunds"); + + b.Navigation("ShoppingCart"); }); modelBuilder.Entity("LiteCharms.Entities.Package", b => @@ -756,20 +757,13 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Navigation("ProductPrices"); }); - modelBuilder.Entity("LiteCharms.Entities.Quote", b => - { - b.Navigation("Order"); - }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => { - b.Navigation("Order"); - - b.Navigation("Packages"); - b.Navigation("Quote"); b.Navigation("ShoppingCartItems"); + + b.Navigation("ShoppingCartPackages"); }); #pragma warning restore 612, 618 } diff --git a/LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.cs b/LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.cs similarity index 86% rename from LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.cs rename to LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.cs index 773f0d6..1220ecc 100644 --- a/LiteCharms.Infrastructure/Database/Migrations/20260510132008_Init.cs +++ b/LiteCharms.Features/Shop/Postgres/Migrations/20260512065421_Init.cs @@ -3,7 +3,7 @@ using Microsoft.EntityFrameworkCore.Migrations; #nullable disable -namespace LiteCharms.Infrastructure.Database.Migrations +namespace LiteCharms.Features.Shop.Postgres.Migrations { /// public partial class Init : Migration @@ -16,7 +16,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), Company = table.Column(type: "text", nullable: true), Name = table.Column(type: "text", nullable: false), @@ -46,7 +46,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), Direction = table.Column(type: "integer", nullable: false), Platform = table.Column(type: "integer", nullable: false), @@ -70,13 +70,34 @@ namespace LiteCharms.Infrastructure.Database.Migrations table.PrimaryKey("PK_Notification", x => x.Id); }); + migrationBuilder.CreateTable( + name: "Package", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + Name = table.Column(type: "text", nullable: false), + Summary = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Description = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: false), + ImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Active = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Package", x => x.Id); + }); + migrationBuilder.CreateTable( name: "Product", columns: table => new { Id = table.Column(type: "uuid", nullable: false), Name = table.Column(type: "text", nullable: false), - Description = table.Column(type: "text", nullable: false), + Summary = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Description = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: false), + ImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Thumbnails = table.Column(type: "jsonb", nullable: true), Active = table.Column(type: "boolean", nullable: false, defaultValue: true) }, constraints: table => @@ -89,7 +110,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), CustomerId = table.Column(type: "uuid", nullable: true), Source = table.Column(type: "text", nullable: true), @@ -116,24 +137,28 @@ namespace LiteCharms.Infrastructure.Database.Migrations }); migrationBuilder.CreateTable( - name: "ShoppingCart", + name: "Order", columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), - CustomerId = table.Column(type: "uuid", nullable: true), - OrderId = table.Column(type: "uuid", nullable: true), - QuoteId = table.Column(type: "uuid", nullable: true) + CustomerId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Requirements = table.Column(type: "jsonb", nullable: true), + Notes = table.Column(type: "jsonb", nullable: true), + Terms = table.Column(type: "jsonb", nullable: true), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true) }, constraints: table => { - table.PrimaryKey("PK_ShoppingCart", x => x.Id); + table.PrimaryKey("PK_Order", x => x.Id); table.ForeignKey( - name: "FK_ShoppingCart_Customer_CustomerId", + name: "FK_Order_Customer_CustomerId", column: x => x.CustomerId, principalTable: "Customer", - principalColumn: "Id"); + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( @@ -141,7 +166,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), ProductId = table.Column(type: "uuid", nullable: false), Price = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), @@ -160,25 +185,78 @@ namespace LiteCharms.Infrastructure.Database.Migrations }); migrationBuilder.CreateTable( - name: "Package", + name: "OrderRefund", columns: table => new { Id = table.Column(type: "uuid", nullable: false), - ShoppingCartId = table.Column(type: "uuid", nullable: true), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + OrderId = table.Column(type: "uuid", nullable: false), + Reason = table.Column(type: "text", nullable: false), + Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderRefund", x => x.Id); + table.ForeignKey( + name: "FK_OrderRefund_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCart", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), - Name = table.Column(type: "text", nullable: false), - Description = table.Column(type: "text", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: false), + OrderId = 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", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ShoppingCart_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "PackageItem", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PackageId = table.Column(type: "uuid", nullable: false), + ProductPriceId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), Active = table.Column(type: "boolean", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Package", x => x.Id); + table.PrimaryKey("PK_PackageItem", x => x.Id); table.ForeignKey( - name: "FK_Package_ShoppingCart_ShoppingCartId", - column: x => x.ShoppingCartId, - principalTable: "ShoppingCart", - principalColumn: "Id"); + name: "FK_PackageItem_Package_PackageId", + column: x => x.PackageId, + principalTable: "Package", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PackageItem_ProductPrice_ProductPriceId", + column: x => x.ProductPriceId, + principalTable: "ProductPrice", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); }); migrationBuilder.CreateTable( @@ -186,13 +264,14 @@ namespace LiteCharms.Infrastructure.Database.Migrations 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), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), ExpiredAt = table.Column(type: "timestamp with time zone", nullable: true), CustomerId = table.Column(type: "uuid", nullable: false), - ShoppingCartId = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: true), + ShoppingCartId = table.Column(type: "uuid", nullable: true), Status = table.Column(type: "integer", nullable: false), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), Reason = table.Column(type: "text", nullable: true) }, constraints: table => @@ -205,9 +284,9 @@ namespace LiteCharms.Infrastructure.Database.Migrations principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( - name: "FK_Quote_Customer_CustomerId1", - column: x => x.CustomerId1, - principalTable: "Customer", + name: "FK_Quote_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", principalColumn: "Id"); table.ForeignKey( name: "FK_Quote_ShoppingCart_ShoppingCartId", @@ -244,38 +323,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "PackageItem", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - PackageId1 = table.Column(type: "uuid", nullable: true), - PackageId = table.Column(type: "uuid", nullable: false), - ProductPriceId = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - Active = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_PackageItem", x => x.Id); - table.ForeignKey( - name: "FK_PackageItem_Package_PackageId", - column: x => x.PackageId, - principalTable: "Package", - principalColumn: "Id"); - table.ForeignKey( - name: "FK_PackageItem_Package_PackageId1", - column: x => x.PackageId1, - principalTable: "Package", - principalColumn: "Id"); - }); - migrationBuilder.CreateTable( name: "ShoppingCartPackage", columns: table => new { Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), ShoppingCartId = table.Column(type: "uuid", nullable: false), PackageId = table.Column(type: "uuid", nullable: false) }, @@ -286,70 +339,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations name: "FK_ShoppingCartPackage_Package_PackageId", column: x => x.PackageId, principalTable: "Package", - principalColumn: "Id"); + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); table.ForeignKey( name: "FK_ShoppingCartPackage_ShoppingCart_ShoppingCartId", column: x => x.ShoppingCartId, principalTable: "ShoppingCart", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "Order", - 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: true), - CustomerId = table.Column(type: "uuid", nullable: false), - QuoteId = table.Column(type: "uuid", nullable: true), - ShoppingCartId = table.Column(type: "uuid", nullable: false), - RefundId = table.Column(type: "uuid", nullable: true), - Status = table.Column(type: "integer", nullable: false), - Requirements = table.Column(type: "jsonb", nullable: true), - Notes = table.Column(type: "jsonb", nullable: true), - Terms = table.Column(type: "jsonb", nullable: true), - DepositRequired = table.Column(type: "boolean", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Order", x => x.Id); - table.ForeignKey( - name: "FK_Order_Customer_CustomerId", - column: x => x.CustomerId, - principalTable: "Customer", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Order_Quote_QuoteId", - column: x => x.QuoteId, - principalTable: "Quote", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - table.ForeignKey( - name: "FK_Order_ShoppingCart_ShoppingCartId", - column: x => x.ShoppingCartId, - principalTable: "ShoppingCart", - principalColumn: "Id"); - }); - - migrationBuilder.CreateTable( - name: "OrderRefund", - columns: table => new - { - Id = table.Column(type: "uuid", nullable: false), - CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), - OrderId = table.Column(type: "uuid", nullable: false), - Reason = table.Column(type: "text", nullable: false), - Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_OrderRefund", x => x.Id); - table.ForeignKey( - name: "FK_OrderRefund_Order_OrderId", - column: x => x.OrderId, - principalTable: "Order", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); @@ -364,28 +359,10 @@ namespace LiteCharms.Infrastructure.Database.Migrations table: "Order", column: "CustomerId"); - 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_OrderRefund_OrderId", table: "OrderRefund", - column: "OrderId", - unique: true); - - migrationBuilder.CreateIndex( - name: "IX_Package_ShoppingCartId", - table: "Package", - column: "ShoppingCartId"); + column: "OrderId"); migrationBuilder.CreateIndex( name: "IX_PackageItem_PackageId", @@ -393,9 +370,9 @@ namespace LiteCharms.Infrastructure.Database.Migrations column: "PackageId"); migrationBuilder.CreateIndex( - name: "IX_PackageItem_PackageId1", + name: "IX_PackageItem_ProductPriceId", table: "PackageItem", - column: "PackageId1"); + column: "ProductPriceId"); migrationBuilder.CreateIndex( name: "IX_ProductPrice_ProductId", @@ -408,9 +385,10 @@ namespace LiteCharms.Infrastructure.Database.Migrations column: "CustomerId"); migrationBuilder.CreateIndex( - name: "IX_Quote_CustomerId1", + name: "IX_Quote_OrderId", table: "Quote", - column: "CustomerId1"); + column: "OrderId", + unique: true); migrationBuilder.CreateIndex( name: "IX_Quote_ShoppingCartId", @@ -423,6 +401,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations table: "ShoppingCart", column: "CustomerId"); + migrationBuilder.CreateIndex( + name: "IX_ShoppingCart_OrderId", + table: "ShoppingCart", + column: "OrderId", + unique: true); + migrationBuilder.CreateIndex( name: "IX_ShoppingCartItems_ProductPriceId", table: "ShoppingCartItems", @@ -459,15 +443,15 @@ namespace LiteCharms.Infrastructure.Database.Migrations migrationBuilder.DropTable( name: "PackageItem"); + migrationBuilder.DropTable( + name: "Quote"); + migrationBuilder.DropTable( name: "ShoppingCartItems"); migrationBuilder.DropTable( name: "ShoppingCartPackage"); - migrationBuilder.DropTable( - name: "Order"); - migrationBuilder.DropTable( name: "ProductPrice"); @@ -475,13 +459,13 @@ namespace LiteCharms.Infrastructure.Database.Migrations name: "Package"); migrationBuilder.DropTable( - name: "Quote"); + name: "ShoppingCart"); migrationBuilder.DropTable( name: "Product"); migrationBuilder.DropTable( - name: "ShoppingCart"); + name: "Order"); migrationBuilder.DropTable( name: "Customer"); diff --git a/LiteCharms.Infrastructure/Database/Migrations/ShopDbContextModelSnapshot.cs b/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs similarity index 86% rename from LiteCharms.Infrastructure/Database/Migrations/ShopDbContextModelSnapshot.cs rename to LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs index 6708099..26d9a29 100644 --- a/LiteCharms.Infrastructure/Database/Migrations/ShopDbContextModelSnapshot.cs +++ b/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs @@ -1,6 +1,6 @@ // using System; -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -8,7 +8,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace LiteCharms.Infrastructure.Database.Migrations +namespace LiteCharms.Features.Shop.Postgres.Migrations { [DbContext(typeof(ShopDbContext))] partial class ShopDbContextModelSnapshot : ModelSnapshot @@ -47,7 +47,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Discord") .HasColumnType("text"); @@ -83,7 +84,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("text"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.Property("Website") @@ -127,7 +127,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); @@ -145,7 +146,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("bigint"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.Property("WebClickId") @@ -173,7 +173,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Direction") .HasColumnType("integer"); @@ -231,7 +232,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("text"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -247,29 +247,22 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); - b.Property("DepositRequired") - .HasColumnType("boolean"); + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.PrimitiveCollection("Notes") .HasColumnType("jsonb"); - b.Property("QuoteId") - .HasColumnType("uuid"); - - b.Property("RefundId") - .HasColumnType("uuid"); - b.PrimitiveCollection("Requirements") .HasColumnType("jsonb"); - b.Property("ShoppingCartId") - .HasColumnType("uuid"); - b.Property("Status") .HasColumnType("integer"); @@ -277,19 +270,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("jsonb"); 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); }); @@ -305,7 +291,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("OrderId") .HasColumnType("uuid"); @@ -316,8 +303,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasKey("Id"); - b.HasIndex("OrderId") - .IsUnique(); + b.HasIndex("OrderId"); b.ToTable("OrderRefund", (string)null); }); @@ -333,27 +319,32 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Description") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.Property("Name") .IsRequired() .HasColumnType("text"); - b.Property("ShoppingCartId") - .HasColumnType("uuid"); + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); - b.HasIndex("ShoppingCartId"); - b.ToTable("Package", (string)null); }); @@ -368,14 +359,12 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("PackageId") .HasColumnType("uuid"); - b.Property("PackageId1") - .HasColumnType("uuid"); - b.Property("ProductPriceId") .HasColumnType("uuid"); @@ -383,7 +372,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasIndex("PackageId"); - b.HasIndex("PackageId1"); + b.HasIndex("ProductPriceId"); b.ToTable("PackageItem", (string)null); }); @@ -401,12 +390,25 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("Description") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); b.Property("Name") .IsRequired() .HasColumnType("text"); + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.PrimitiveCollection("Thumbnails") + .HasColumnType("jsonb"); + b.HasKey("Id"); b.ToTable("Product", (string)null); @@ -423,7 +425,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("Discount") .HasPrecision(18, 2) @@ -437,7 +440,6 @@ namespace LiteCharms.Infrastructure.Database.Migrations .HasColumnType("uuid"); b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -455,35 +457,40 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("CustomerId") .HasColumnType("uuid"); - b.Property("CustomerId1") - .HasColumnType("uuid"); - b.Property("ExpiredAt") .HasColumnType("timestamp with time zone"); + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("OrderId") + .HasColumnType("uuid"); + b.Property("Reason") .HasColumnType("text"); - b.Property("ShoppingCartId") + 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("CustomerId1"); + b.HasIndex("OrderId") + .IsUnique(); b.HasIndex("ShoppingCartId") .IsUnique(); @@ -499,25 +506,25 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); - b.Property("CustomerId") + b.Property("CustomerId") .HasColumnType("uuid"); b.Property("OrderId") .HasColumnType("uuid"); - b.Property("QuoteId") - .HasColumnType("uuid"); - b.Property("UpdatedAt") - .ValueGeneratedOnUpdate() .HasColumnType("timestamp with time zone"); b.HasKey("Id"); b.HasIndex("CustomerId"); + b.HasIndex("OrderId") + .IsUnique(); + b.ToTable("ShoppingCart", (string)null); }); @@ -559,7 +566,8 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Property("CreatedAt") .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone"); + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("PackageId") .HasColumnType("uuid"); @@ -580,8 +588,7 @@ namespace LiteCharms.Infrastructure.Database.Migrations { b.HasOne("LiteCharms.Entities.Customer", "Customer") .WithMany("Leads") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.NoAction); + .HasForeignKey("CustomerId"); b.Navigation("Customer"); }); @@ -594,55 +601,37 @@ namespace LiteCharms.Infrastructure.Database.Migrations .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") + .WithMany("Refunds") + .HasForeignKey("OrderId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Order"); }); - modelBuilder.Entity("LiteCharms.Entities.Package", b => - { - b.HasOne("LiteCharms.Entities.ShoppingCart", null) - .WithMany("Packages") - .HasForeignKey("ShoppingCartId"); - }); - modelBuilder.Entity("LiteCharms.Entities.PackageItem", b => { b.HasOne("LiteCharms.Entities.Package", "Package") - .WithMany() + .WithMany("PackageItems") .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Package", null) - .WithMany("PackageItems") - .HasForeignKey("PackageId1"); + b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); b.Navigation("Package"); + + b.Navigation("ProductPrice"); }); modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => @@ -659,23 +648,23 @@ namespace LiteCharms.Infrastructure.Database.Migrations modelBuilder.Entity("LiteCharms.Entities.Quote", b => { b.HasOne("LiteCharms.Entities.Customer", "Customer") - .WithMany() + .WithMany("Quotes") .HasForeignKey("CustomerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Customer", null) - .WithMany("Quotes") - .HasForeignKey("CustomerId1"); + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Entities.Quote", "OrderId"); b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") .WithOne("Quote") - .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId") - .OnDelete(DeleteBehavior.NoAction) - .IsRequired(); + .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId"); b.Navigation("Customer"); + b.Navigation("Order"); + b.Navigation("ShoppingCart"); }); @@ -684,9 +673,17 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasOne("LiteCharms.Entities.Customer", "Customer") .WithMany("ShoppingCarts") .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.NoAction); + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Entities.Order", "Order") + .WithOne("ShoppingCart") + .HasForeignKey("LiteCharms.Entities.ShoppingCart", "OrderId") + .OnDelete(DeleteBehavior.SetNull); b.Navigation("Customer"); + + b.Navigation("Order"); }); modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => @@ -713,13 +710,13 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.HasOne("LiteCharms.Entities.Package", "Package") .WithMany() .HasForeignKey("PackageId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") - .WithMany() + .WithMany("ShoppingCartPackages") .HasForeignKey("ShoppingCartId") - .OnDelete(DeleteBehavior.NoAction) + .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Package"); @@ -740,7 +737,11 @@ namespace LiteCharms.Infrastructure.Database.Migrations modelBuilder.Entity("LiteCharms.Entities.Order", b => { - b.Navigation("Refund"); + b.Navigation("Quote"); + + b.Navigation("Refunds"); + + b.Navigation("ShoppingCart"); }); modelBuilder.Entity("LiteCharms.Entities.Package", b => @@ -753,20 +754,13 @@ namespace LiteCharms.Infrastructure.Database.Migrations b.Navigation("ProductPrices"); }); - modelBuilder.Entity("LiteCharms.Entities.Quote", b => - { - b.Navigation("Order"); - }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => { - b.Navigation("Order"); - - b.Navigation("Packages"); - b.Navigation("Quote"); b.Navigation("ShoppingCartItems"); + + b.Navigation("ShoppingCartPackages"); }); #pragma warning restore 612, 618 } diff --git a/LiteCharms.Infrastructure/Database/ShopDbContext.cs b/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs similarity index 64% rename from LiteCharms.Infrastructure/Database/ShopDbContext.cs rename to LiteCharms.Features/Shop/Postgres/ShopDbContext.cs index c3d0679..b02f630 100644 --- a/LiteCharms.Infrastructure/Database/ShopDbContext.cs +++ b/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs @@ -1,6 +1,13 @@ -using LiteCharms.Entities; +using LiteCharms.Features.Shop.CartPackages.Entities; +using LiteCharms.Features.Shop.Customers.Entities; +using LiteCharms.Features.Shop.Leads.Entities; +using LiteCharms.Features.Shop.Notifications.Entities; +using LiteCharms.Features.Shop.Orders.Entities; +using LiteCharms.Features.Shop.Products.Entities; +using LiteCharms.Features.Shop.Quotes.Entities; +using LiteCharms.Features.Shop.ShoppingCarts.Entities; -namespace LiteCharms.Infrastructure.Database; +namespace LiteCharms.Features.Shop.Postgres; public class ShopDbContext(DbContextOptions options) : DbContext(options) { diff --git a/LiteCharms.Infrastructure/Database/ShopDbContextFactory.cs b/LiteCharms.Features/Shop/Postgres/ShopDbContextFactory.cs similarity index 93% rename from LiteCharms.Infrastructure/Database/ShopDbContextFactory.cs rename to LiteCharms.Features/Shop/Postgres/ShopDbContextFactory.cs index 9de3cda..b8437d9 100644 --- a/LiteCharms.Infrastructure/Database/ShopDbContextFactory.cs +++ b/LiteCharms.Features/Shop/Postgres/ShopDbContextFactory.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Infrastructure.Database; +namespace LiteCharms.Features.Shop.Postgres; public class ShopDbContextFactory : IDesignTimeDbContextFactory { diff --git a/LiteCharms.Entities/Product.cs b/LiteCharms.Features/Shop/Products/Entities/Product.cs similarity index 69% rename from LiteCharms.Entities/Product.cs rename to LiteCharms.Features/Shop/Products/Entities/Product.cs index 6b46ff7..5aa0b99 100644 --- a/LiteCharms.Entities/Product.cs +++ b/LiteCharms.Features/Shop/Products/Entities/Product.cs @@ -1,6 +1,4 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Products.Entities; [EntityTypeConfiguration] public class Product : Models.Product diff --git a/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs b/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs new file mode 100644 index 0000000..ee2fb68 --- /dev/null +++ b/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs @@ -0,0 +1,17 @@ +namespace LiteCharms.Features.Shop.Products.Entities; + +public class ProductConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable(nameof(Product)); + + builder.HasKey(f => f.Id); + builder.Property(f => f.Name).IsRequired(); + builder.Property(f => f.Summary).IsRequired().HasMaxLength(512); + builder.Property(f => f.Description).IsRequired().HasMaxLength(2048); + builder.Property(f => f.ImageUrl).IsRequired(false).HasMaxLength(2048); + builder.Property(f => f.Thumbnails).HasColumnType("jsonb").IsRequired(false); + builder.Property(f => f.Active).HasDefaultValue(true); + } +} diff --git a/LiteCharms.Entities/ProductPrice.cs b/LiteCharms.Features/Shop/Products/Entities/ProductPrice.cs similarity index 69% rename from LiteCharms.Entities/ProductPrice.cs rename to LiteCharms.Features/Shop/Products/Entities/ProductPrice.cs index db492ed..a711cae 100644 --- a/LiteCharms.Entities/ProductPrice.cs +++ b/LiteCharms.Features/Shop/Products/Entities/ProductPrice.cs @@ -1,6 +1,4 @@ -using LiteCharms.Entities.Configuration; - -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Products.Entities; [EntityTypeConfiguration] public class ProductPrice : Models.ProductPrice diff --git a/LiteCharms.Entities/Configuration/ProductPriceConfiguration.cs b/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs similarity index 70% rename from LiteCharms.Entities/Configuration/ProductPriceConfiguration.cs rename to LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs index 5e7cfc9..a8daa83 100644 --- a/LiteCharms.Entities/Configuration/ProductPriceConfiguration.cs +++ b/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.Products.Entities; public class ProductPriceConfiguration : IEntityTypeConfiguration { @@ -7,16 +7,17 @@ public class ProductPriceConfiguration : IEntityTypeConfiguration builder.ToTable(nameof(ProductPrice)); builder.HasKey(f => f.Id); - builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd(); - builder.Property(f => f.UpdatedAt).IsRequired(false).ValueGeneratedOnUpdate(); + builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); builder.Property(f => f.ProductId).IsRequired(); builder.Property(f => f.Price).IsRequired().HasPrecision(18, 2); builder.Property(f => f.Discount).HasPrecision(18, 2); builder.Property(f => f.Active); builder.HasOne(f => f.Product) - .WithMany(p => p.ProductPrices) - .HasForeignKey(f => f.ProductId) + .WithMany(f => f.ProductPrices) + .HasForeignKey(pp => pp.ProductId) + .IsRequired() .OnDelete(DeleteBehavior.Restrict); } } diff --git a/LiteCharms.Features/Shop/Products/Models/Product.cs b/LiteCharms.Features/Shop/Products/Models/Product.cs new file mode 100644 index 0000000..8fdc778 --- /dev/null +++ b/LiteCharms.Features/Shop/Products/Models/Product.cs @@ -0,0 +1,18 @@ +namespace LiteCharms.Features.Shop.Products.Models; + +public class Product +{ + public Guid Id { get; set; } + + public string? Name { get; set; } + + public string? Summary { get; set; } + + public string? Description { get; set; } + + public string? ImageUrl { get; set; } + + public string[]? Thumbnails { get; set; } + + public bool Active { get; set; } +} diff --git a/LiteCharms.Models/ProductPrice.cs b/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs similarity index 85% rename from LiteCharms.Models/ProductPrice.cs rename to LiteCharms.Features/Shop/Products/Models/ProductPrice.cs index 49931ea..1f44247 100644 --- a/LiteCharms.Models/ProductPrice.cs +++ b/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Products.Models; public class ProductPrice { diff --git a/LiteCharms.Features/Products/Queries/GetProductPriceQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs similarity index 90% rename from LiteCharms.Features/Products/Queries/GetProductPriceQuery.cs rename to LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs index 347d759..f118bcc 100644 --- a/LiteCharms.Features/Products/Queries/GetProductPriceQuery.cs +++ b/LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries; diff --git a/LiteCharms.Features/Products/Queries/GetProductPricesQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs similarity index 90% rename from LiteCharms.Features/Products/Queries/GetProductPricesQuery.cs rename to LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs index 6e9cf66..5ba4be2 100644 --- a/LiteCharms.Features/Products/Queries/GetProductPricesQuery.cs +++ b/LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries; diff --git a/LiteCharms.Features/Products/Queries/GetProductQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs similarity index 89% rename from LiteCharms.Features/Products/Queries/GetProductQuery.cs rename to LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs index 2f5b2a8..cd811ac 100644 --- a/LiteCharms.Features/Products/Queries/GetProductQuery.cs +++ b/LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries; diff --git a/LiteCharms.Features/Products/Queries/GetProductsQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs similarity index 89% rename from LiteCharms.Features/Products/Queries/GetProductsQuery.cs rename to LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs index 1c7082d..a60670d 100644 --- a/LiteCharms.Features/Products/Queries/GetProductsQuery.cs +++ b/LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries; diff --git a/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs rename to LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs index f73bced..c1383de 100644 --- a/LiteCharms.Features/Products/Queries/Handlers/GetProductPriceQueryHandler.cs +++ b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries.Handlers; diff --git a/LiteCharms.Features/Products/Queries/Handlers/GetProductPricesQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs similarity index 91% rename from LiteCharms.Features/Products/Queries/Handlers/GetProductPricesQueryHandler.cs rename to LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs index c9eac0a..1bc5489 100644 --- a/LiteCharms.Features/Products/Queries/Handlers/GetProductPricesQueryHandler.cs +++ b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries.Handlers; diff --git a/LiteCharms.Features/Products/Queries/Handlers/GetProductQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs similarity index 90% rename from LiteCharms.Features/Products/Queries/Handlers/GetProductQueryHandler.cs rename to LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs index 6cf09c6..d60591e 100644 --- a/LiteCharms.Features/Products/Queries/Handlers/GetProductQueryHandler.cs +++ b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries.Handlers; diff --git a/LiteCharms.Features/Products/Queries/Handlers/GetProductsQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs similarity index 90% rename from LiteCharms.Features/Products/Queries/Handlers/GetProductsQueryHandler.cs rename to LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs index 3fc0366..0ad9f40 100644 --- a/LiteCharms.Features/Products/Queries/Handlers/GetProductsQueryHandler.cs +++ b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Products.Models; namespace LiteCharms.Features.Products.Queries.Handlers; diff --git a/LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs similarity index 100% rename from LiteCharms.Features/Quotes/Commands/AssignQuoteToOrderCommand.cs rename to LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs diff --git a/LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs rename to LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs similarity index 97% rename from LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs rename to LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs index 3145cd4..266e8d9 100644 --- a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Quotes.Commands.Handlers; diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs similarity index 96% rename from LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs index 443c907..a9f6b12 100644 --- a/LiteCharms.Features/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Quotes.Commands.Handlers; diff --git a/LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs similarity index 95% rename from LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs rename to LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs index a279370..8e80a52 100644 --- a/LiteCharms.Features/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.Quotes.Commands.Handlers; diff --git a/LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs similarity index 90% rename from LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs rename to LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs index 901bc46..14c9684 100644 --- a/LiteCharms.Features/Quotes/Commands/UpdateQuoteStatusCommand.cs +++ b/LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs @@ -1,4 +1,5 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop; +using LiteCharms.Models; namespace LiteCharms.Features.Quotes.Commands; diff --git a/LiteCharms.Entities/Quote.cs b/LiteCharms.Features/Shop/Quotes/Entities/Quote.cs similarity index 54% rename from LiteCharms.Entities/Quote.cs rename to LiteCharms.Features/Shop/Quotes/Entities/Quote.cs index 8bacd89..4ea6dab 100644 --- a/LiteCharms.Entities/Quote.cs +++ b/LiteCharms.Features/Shop/Quotes/Entities/Quote.cs @@ -1,13 +1,15 @@ -using LiteCharms.Entities.Configuration; +using LiteCharms.Features.Shop.Customers.Entities; +using LiteCharms.Features.Shop.Orders.Entities; +using LiteCharms.Features.Shop.ShoppingCarts.Entities; -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.Quotes.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; } + + public virtual ShoppingCart? ShoppingCart { get; set; } } diff --git a/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs b/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs new file mode 100644 index 0000000..299044e --- /dev/null +++ b/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs @@ -0,0 +1,33 @@ +namespace LiteCharms.Features.Shop.Quotes.Entities; + +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().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.ExpiredAt).IsRequired(false); + builder.Property(f => f.CustomerId).IsRequired(); + builder.Property(f => f.OrderId); + builder.Property(f => f.ShoppingCartId); + builder.Property(f => f.Status).IsRequired().HasConversion(); + builder.Property(f => f.InvoiceUrl).IsRequired(false).HasMaxLength(2048); + builder.Property(f => f.Reason).IsRequired(false); + + builder.HasOne(q => q.Customer) + .WithMany(c => c.Quotes) + .HasForeignKey(q => q.CustomerId) + .IsRequired(); + + builder.HasOne(q => q.Order) + .WithOne(o => o.Quote) + .HasForeignKey(q => q.OrderId); + + builder.HasOne(q => q.ShoppingCart) + .WithOne(o => o.Quote) + .HasForeignKey(q => q.ShoppingCartId); + } +} diff --git a/LiteCharms.Models/Quote.cs b/LiteCharms.Features/Shop/Quotes/Models/Quote.cs similarity index 64% rename from LiteCharms.Models/Quote.cs rename to LiteCharms.Features/Shop/Quotes/Models/Quote.cs index 955d0e7..5422348 100644 --- a/LiteCharms.Models/Quote.cs +++ b/LiteCharms.Features/Shop/Quotes/Models/Quote.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.Quotes.Models; public class Quote { @@ -12,9 +12,13 @@ public class Quote public Guid CustomerId { get; set; } - public Guid ShoppingCartId { get; set; } + public Guid? OrderId { get; set; } + + public Guid? ShoppingCartId { get; set; } public QuoteStatus Status { get; set; } + public string? InvoiceUrl { get; set; } + public string? Reason { get; set; } } diff --git a/LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs similarity index 90% rename from LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs rename to LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs index d531b3a..37adc17 100644 --- a/LiteCharms.Features/Quotes/Queries/GetCustomerQuotesQuery.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries; diff --git a/LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs similarity index 89% rename from LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs rename to LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs index 0f566cf..756788e 100644 --- a/LiteCharms.Features/Quotes/Queries/GetQuoteQuery.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries; diff --git a/LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs similarity index 93% rename from LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs rename to LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs index 20a8d4b..4e7db83 100644 --- a/LiteCharms.Features/Quotes/Queries/GetQuotesQuery.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries; diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs similarity index 93% rename from LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs rename to LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs index 11c2169..811df36 100644 --- a/LiteCharms.Features/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs @@ -1,7 +1,7 @@ using LiteCharms.Extensions; using LiteCharms.Features.Quotes.Queries; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries.Handlers; diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs similarity index 91% rename from LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs rename to LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs index 8986161..35c2d39 100644 --- a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries.Handlers; diff --git a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs similarity index 93% rename from LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs rename to LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs index 3d548b0..f7ad77b 100644 --- a/LiteCharms.Features/Quotes/Queries/Handlers/GetQuotesHandler.cs +++ b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Quotes.Models; namespace LiteCharms.Features.Quotes.Queries.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/CreateShoppingCartCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs similarity index 97% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs index dc4a50d..7a2cd2c 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs similarity index 97% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs index 67e7949..a634375 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs similarity index 96% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs index 241af60..2d51f2e 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs similarity index 96% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs index 3f3ce2f..0043a5f 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs similarity index 97% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs index 6294199..2bd5b3a 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs similarity index 97% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs index 73255c5..442c944 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs similarity index 96% rename from LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs index 42b362f..3e54242 100644 --- a/LiteCharms.Features/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs @@ -1,4 +1,4 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs diff --git a/LiteCharms.Features/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs similarity index 100% rename from LiteCharms.Features/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs diff --git a/LiteCharms.Entities/ShoppingCart.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCart.cs similarity index 54% rename from LiteCharms.Entities/ShoppingCart.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCart.cs index ead1175..cfb4bd5 100644 --- a/LiteCharms.Entities/ShoppingCart.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCart.cs @@ -1,6 +1,8 @@ -using LiteCharms.Entities.Configuration; +using LiteCharms.Features.Shop.Customers.Entities; +using LiteCharms.Features.Shop.Orders.Entities; +using LiteCharms.Features.Shop.Quotes.Entities; -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; [EntityTypeConfiguration] public class ShoppingCart : Models.ShoppingCart @@ -13,5 +15,5 @@ public class ShoppingCart : Models.ShoppingCart public virtual ICollection? ShoppingCartItems { get; set; } - public virtual ICollection? Packages { get; set; } + public virtual ICollection? ShoppingCartPackages { get; set; } } diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs new file mode 100644 index 0000000..e45a04b --- /dev/null +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs @@ -0,0 +1,26 @@ +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; + +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().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.CustomerId).IsRequired(); + builder.Property(f => f.OrderId); + + builder.HasOne(s => s.Customer) + .WithMany(c => c.ShoppingCarts) + .HasForeignKey(s => s.CustomerId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(s => s.Order) + .WithOne(o => o.ShoppingCart) + .HasForeignKey(s => s.OrderId) + .OnDelete(DeleteBehavior.SetNull); + } +} diff --git a/LiteCharms.Entities/ShoppingCartItem.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs similarity index 61% rename from LiteCharms.Entities/ShoppingCartItem.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs index d5e5634..50afb9d 100644 --- a/LiteCharms.Entities/ShoppingCartItem.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs @@ -1,4 +1,6 @@ -namespace LiteCharms.Entities; +using LiteCharms.Features.Shop.Products.Entities; + +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; public class ShoppingCartItem : Models.ShoppingCartItem { diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs new file mode 100644 index 0000000..3dbc5f7 --- /dev/null +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs @@ -0,0 +1,30 @@ +using LiteCharms.Features.Shop.Products.Entities; + +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; + +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().HasDefaultValueSql("now()"); + builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.Quantity).IsRequired().HasDefaultValue(1); + builder.Property(f => f.ShoppingCartId).IsRequired(); + builder.Property(f => f.ProductPriceId).IsRequired(); + + builder.HasOne() + .WithMany(s => s.ShoppingCartItems) + .HasForeignKey(f => f.ShoppingCartId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne() + .WithMany() + .HasForeignKey(f => f.ProductPriceId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); + } +} diff --git a/LiteCharms.Entities/ShoppingCartPackage.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackage.cs similarity index 69% rename from LiteCharms.Entities/ShoppingCartPackage.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackage.cs index 45a3831..4adf983 100644 --- a/LiteCharms.Entities/ShoppingCartPackage.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackage.cs @@ -1,6 +1,6 @@ -using LiteCharms.Entities.Configuration; +using LiteCharms.Features.Shop.CartPackages.Entities; -namespace LiteCharms.Entities; +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; [EntityTypeConfiguration] public class ShoppingCartPackage : Models.ShoppingCartPackage diff --git a/LiteCharms.Entities/Configuration/ShoppingCartPackageConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs similarity index 54% rename from LiteCharms.Entities/Configuration/ShoppingCartPackageConfiguration.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs index 7d2f296..ee08e91 100644 --- a/LiteCharms.Entities/Configuration/ShoppingCartPackageConfiguration.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Entities.Configuration; +namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; public class ShoppingCartPackageConfiguration : IEntityTypeConfiguration { @@ -7,18 +7,20 @@ public class ShoppingCartPackageConfiguration : IEntityTypeConfiguration f.Id); - builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd(); + builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); builder.Property(f => f.ShoppingCartId).IsRequired(); builder.Property(f => f.PackageId).IsRequired(); - builder.HasOne(f => f.Package) - .WithMany() - .HasForeignKey(f => f.PackageId) - .OnDelete(DeleteBehavior.NoAction); - builder.HasOne(f => f.ShoppingCart) - .WithMany() - .HasForeignKey(f => f.ShoppingCartId) - .OnDelete(DeleteBehavior.NoAction); + .WithMany(s => s.ShoppingCartPackages) + .HasForeignKey(scp => scp.ShoppingCartId) + .IsRequired() + .OnDelete(DeleteBehavior.Cascade); + + builder.HasOne(f => f.Package) + .WithMany() + .HasForeignKey(scp => scp.PackageId) + .IsRequired() + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/LiteCharms.Models/ShoppingCart.cs b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs similarity index 64% rename from LiteCharms.Models/ShoppingCart.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs index 53e27b9..fe6f633 100644 --- a/LiteCharms.Models/ShoppingCart.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.ShoppingCarts.Models; public class ShoppingCart { @@ -8,9 +8,7 @@ public class ShoppingCart public DateTimeOffset? UpdatedAt { get; set; } - public Guid? CustomerId { 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.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs similarity index 83% rename from LiteCharms.Models/ShoppingCartItem.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs index 3530515..8dcc0be 100644 --- a/LiteCharms.Models/ShoppingCartItem.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.ShoppingCarts.Models; public class ShoppingCartItem { diff --git a/LiteCharms.Models/ShoppingCartPackage.cs b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs similarity index 77% rename from LiteCharms.Models/ShoppingCartPackage.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs index 673b577..fdc6d7b 100644 --- a/LiteCharms.Models/ShoppingCartPackage.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Models; +namespace LiteCharms.Features.Shop.ShoppingCarts.Models; public class ShoppingCartPackage { diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs similarity index 90% rename from LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs index 0d64287..e901ee3 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs similarity index 90% rename from LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs index 197d475..e6ddec3 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs similarity index 90% rename from LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs index 2f64359..611b48f 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs similarity index 90% rename from LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs index 07dfc5a..1ef9784 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/GetShoppingCartQuery.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs @@ -1,4 +1,4 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs index d6bde0f..8554fc6 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs @@ -1,7 +1,7 @@ using LiteCharms.Extensions; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.ShoppingCarts.Models; using LiteCharms.Features.ShoppingCarts.Queries; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs similarity index 93% rename from LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs index 0e7104c..0082f85 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs similarity index 93% rename from LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs index d6727ee..68e17cf 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; diff --git a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs similarity index 91% rename from LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs rename to LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs index 6793e22..2cf05a9 100644 --- a/LiteCharms.Features/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs @@ -1,6 +1,6 @@ using LiteCharms.Extensions; -using LiteCharms.Infrastructure.Database; -using LiteCharms.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.ShoppingCarts.Models; namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; diff --git a/LiteCharms.Features/Utilities/Hash/Commands/ComputeHashCommand.cs b/LiteCharms.Features/Utilities/Hash/Commands/ComputeHashCommand.cs deleted file mode 100644 index 252a9e7..0000000 --- a/LiteCharms.Features/Utilities/Hash/Commands/ComputeHashCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LiteCharms.Features.Utilities.Hash.Commands; - -public class ComputeHashCommand : IRequest> -{ - public string? Input { get; set; } - - private ComputeHashCommand(string input) => Input = input; - - public static ComputeHashCommand Create(string input) - { - if(string.IsNullOrWhiteSpace(input)) - throw new ArgumentException("Input is required", nameof(input)); - - return new(input); - } -} diff --git a/LiteCharms.Features/Utilities/Hash/Commands/Handlers/ComputeHashCommandHandler.cs b/LiteCharms.Features/Utilities/Hash/Commands/Handlers/ComputeHashCommandHandler.cs deleted file mode 100644 index 013ab03..0000000 --- a/LiteCharms.Features/Utilities/Hash/Commands/Handlers/ComputeHashCommandHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using LiteCharms.Features.Utilities.Hash.Commands; - -namespace LiteCharms.Features.Utilities.Hash.Commands.Handlers; - -public class ComputeHashCommandHandler : IRequestHandler> -{ - public async ValueTask> Handle(ComputeHashCommand request, CancellationToken cancellationToken) - { - try - { - var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(request.Input!)); - - return Result.Ok(Convert.ToHexString(bytes)); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Infrastructure/LiteCharms.Infrastructure.csproj b/LiteCharms.Infrastructure/LiteCharms.Infrastructure.csproj deleted file mode 100644 index b387b65..0000000 --- a/LiteCharms.Infrastructure/LiteCharms.Infrastructure.csproj +++ /dev/null @@ -1,109 +0,0 @@ - - - - net10.0 - enable - enable - 7770ab3b-72ee-4897-8e06-57d6613e050a - True - ..\LiteCharms.snk - - - - - LiteCharms.Infrastructure - 1.0.20 - Khwezi Mngoma - Lite Charms (PTY) Ltd - Infrastructure components for Lite Charms applications. - https://gitea.khongisa.co.za/litecharms/components - https://gitea.khongisa.co.za/litecharms/components.git - git - LICENSE - icon.png - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - - - - - - diff --git a/LiteCharms.Infrastructure/ServiceBus/Exchanges/EmailExchange.cs b/LiteCharms.Infrastructure/ServiceBus/Exchanges/EmailExchange.cs deleted file mode 100644 index 145ce46..0000000 --- a/LiteCharms.Infrastructure/ServiceBus/Exchanges/EmailExchange.cs +++ /dev/null @@ -1,37 +0,0 @@ -using LiteCharms.Infrastructure.ServiceBus.Queues; - -namespace LiteCharms.Infrastructure.ServiceBus.Exchanges; - -public class EmailExchange(EmailQueue messages, ILogger logger, IPublisher mediator) : BackgroundService -{ - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { - while (messages.Incoming.TryRead(out var message)) - { - try - { - switch (message.Name) - { - case "SendShopEmailEnquiryEvent": - await mediator.Publish(message, stoppingToken); - break; - case "ProcessEmailNotificationsEvent": - await mediator.Publish(message, stoppingToken); - break; - default: - logger.LogWarning("Unsupported email event {Event}", message.Name); - break; - } - } - catch (Exception ex) - { - logger.LogError(ex, ex.Message); - } - } - - await Task.Delay(1000, stoppingToken); - } - } -} diff --git a/LiteCharms.Infrastructure/ServiceBus/Queues/EmailQueue.cs b/LiteCharms.Infrastructure/ServiceBus/Queues/EmailQueue.cs deleted file mode 100644 index 24b161e..0000000 --- a/LiteCharms.Infrastructure/ServiceBus/Queues/EmailQueue.cs +++ /dev/null @@ -1,5 +0,0 @@ -using LiteCharms.Abstractions; - -namespace LiteCharms.Infrastructure.ServiceBus.Queues; - -public class EmailQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Infrastructure/ServiceBus/Queues/GeneralQueue.cs b/LiteCharms.Infrastructure/ServiceBus/Queues/GeneralQueue.cs deleted file mode 100644 index f7024f6..0000000 --- a/LiteCharms.Infrastructure/ServiceBus/Queues/GeneralQueue.cs +++ /dev/null @@ -1,5 +0,0 @@ -using LiteCharms.Abstractions; - -namespace LiteCharms.Infrastructure.ServiceBus.Queues; - -public class GeneralQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Infrastructure/ServiceBus/Queues/SalesQueue.cs b/LiteCharms.Infrastructure/ServiceBus/Queues/SalesQueue.cs deleted file mode 100644 index 714ac11..0000000 --- a/LiteCharms.Infrastructure/ServiceBus/Queues/SalesQueue.cs +++ /dev/null @@ -1,5 +0,0 @@ -using LiteCharms.Abstractions; - -namespace LiteCharms.Infrastructure.ServiceBus.Queues; - -public class SalesQueue : EventBusQueueBase, IEventBusQueue; diff --git a/LiteCharms.Infrastructure/appsettings.json b/LiteCharms.Infrastructure/appsettings.json deleted file mode 100644 index 0db3279..0000000 --- a/LiteCharms.Infrastructure/appsettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - -} diff --git a/LiteCharms.Models/LiteCharms.Models.csproj b/LiteCharms.Models/LiteCharms.Models.csproj deleted file mode 100644 index 95e00d1..0000000 --- a/LiteCharms.Models/LiteCharms.Models.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - net10.0 - enable - enable - True - ..\LiteCharms.snk - - - - - LiteCharms.Models - 1.0.20 - Khwezi Mngoma - Lite Charms (PTY) Ltd - Shared models for Lite Charms applications. - https://gitea.khongisa.co.za/litecharms/components - https://gitea.khongisa.co.za/litecharms/components.git - git - LICENSE - utility;dotnet - icon.png - - - - - - - - - - - - diff --git a/LiteCharms.Models/Product.cs b/LiteCharms.Models/Product.cs deleted file mode 100644 index 406452c..0000000 --- a/LiteCharms.Models/Product.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace LiteCharms.Models; - -public class Product -{ - public Guid Id { get; set; } - - public string? Name { get; set; } - - public string? Description { get; set; } - - public bool Active { get; set; } -} diff --git a/LiteCharmsShared.slnx b/LiteCharmsShared.slnx index 05f4368..21e11b6 100644 --- a/LiteCharmsShared.slnx +++ b/LiteCharmsShared.slnx @@ -2,10 +2,6 @@ - - - + - - -- 2.47.3 From 42001998d6850b6eedab555e7bffeae30f499225 Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Wed, 13 May 2026 20:15:21 +0200 Subject: [PATCH 2/5] Fixed package references and namespaces Refactored mappers --- LiteCharms.Features.Tests/CommonFixture.cs | 2 +- LiteCharms.Features/Extensions/Constants.cs | 5 -- .../Extensions/EntityModeMappers.cs | 72 +++++++++++-------- .../Extensions/HealthChecks.cs | 4 +- LiteCharms.Features/Extensions/Monitoring.cs | 2 +- LiteCharms.Features/Extensions/Postgres.cs | 4 +- LiteCharms.Features/Extensions/Quartz.cs | 6 +- LiteCharms.Features/Extensions/ServiceBus.cs | 10 +-- .../LiteCharms.Features.csproj | 1 + 9 files changed, 56 insertions(+), 50 deletions(-) delete mode 100644 LiteCharms.Features/Extensions/Constants.cs diff --git a/LiteCharms.Features.Tests/CommonFixture.cs b/LiteCharms.Features.Tests/CommonFixture.cs index b237b8f..358aaf2 100644 --- a/LiteCharms.Features.Tests/CommonFixture.cs +++ b/LiteCharms.Features.Tests/CommonFixture.cs @@ -1,4 +1,4 @@ -using LiteCharms.Extensions; +using LiteCharms.Features.Extensions; namespace LiteCharms.Features.Tests; diff --git a/LiteCharms.Features/Extensions/Constants.cs b/LiteCharms.Features/Extensions/Constants.cs deleted file mode 100644 index b9d11e1..0000000 --- a/LiteCharms.Features/Extensions/Constants.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace LiteCharms.Features.Extensions; - -public static class Constants -{ -} diff --git a/LiteCharms.Features/Extensions/EntityModeMappers.cs b/LiteCharms.Features/Extensions/EntityModeMappers.cs index 9a1d97a..baeb69c 100644 --- a/LiteCharms.Features/Extensions/EntityModeMappers.cs +++ b/LiteCharms.Features/Extensions/EntityModeMappers.cs @@ -1,29 +1,36 @@ -using LiteCharms.Models; +using LiteCharms.Features.Shop.CartPackages.Models; +using LiteCharms.Features.Shop.Customers.Models; +using LiteCharms.Features.Shop.Leads.Models; +using LiteCharms.Features.Shop.Notifications.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Products.Models; +using LiteCharms.Features.Shop.Quotes.Models; +using LiteCharms.Features.Shop.ShoppingCarts.Models; -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class EntityModeMappers { - public static ShoppingCartPackage ToModel(this Entities.ShoppingCartPackage entity) => + public static ShoppingCartPackage ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartPackage entity) => new() { Id = entity.Id, CreatedAt = entity.CreatedAt, PackageId = entity.PackageId, - ShoppingCartId = entity.ShoppingCartId + ShoppingCartId = entity.ShoppingCartId }; - public static PackageItem ToModel(this Entities.PackageItem entity) => + public static PackageItem ToModel(this Shop.CartPackages.Entities.PackageItem entity) => new() { Id = entity.Id, Active = entity.Active, CreatedAt = entity.CreatedAt, PackageId = entity.PackageId, - ProductPriceId = entity.ProductPriceId + ProductPriceId = entity.ProductPriceId }; - public static Package ToModel(this Entities.Package entity) => + public static Package ToModel(this Shop.CartPackages.Entities.Package entity) => new() { Id = entity.Id, @@ -31,10 +38,12 @@ public static class EntityModeMappers Active = entity.Active, Description = entity.Description, Name = entity.Name, - UpdatedAt = entity.UpdatedAt + UpdatedAt = entity.UpdatedAt, + ImageUrl = entity.ImageUrl, + Summary = entity.Summary }; - public static ShoppingCartItem ToModel(this Entities.ShoppingCartItem entity) => + public static ShoppingCartItem ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartItem entity) => new() { Id = entity.Id, @@ -42,21 +51,20 @@ public static class EntityModeMappers UpdatedAt = entity.UpdatedAt, ProductPriceId = entity.ProductPriceId, Quantity = entity.Quantity, - ShoppingCartId = entity.ShoppingCartId + ShoppingCartId = entity.ShoppingCartId }; - public static ShoppingCart ToModel(this Entities.ShoppingCart entity) => + public static ShoppingCart ToModel(this Shop.ShoppingCarts.Entities.ShoppingCart entity) => new() { Id = entity.Id, CreatedAt = entity.CreatedAt, UpdatedAt = entity.UpdatedAt, CustomerId = entity.CustomerId, - OrderId = entity.OrderId, - QuoteId = entity.QuoteId + OrderId = entity.OrderId }; - public static Quote ToModel(this Entities.Quote entity) => + public static Quote ToModel(this Shop.Quotes.Entities.Quote entity) => new() { Id = entity.Id, @@ -66,10 +74,12 @@ public static class EntityModeMappers ExpiredAt = entity.ExpiredAt, Reason = entity.Reason, ShoppingCartId = entity.ShoppingCartId, - Status = entity.Status + Status = entity.Status, + InvoiceUrl = entity.InvoiceUrl, + OrderId = entity.OrderId }; - public static Notification ToModel(this Entities.Notification entity) => + public static Notification ToModel(this Shop.Notifications.Entities.Notification entity) => new() { Id = entity.Id, @@ -93,7 +103,7 @@ public static class EntityModeMappers Errors = entity.Errors }; - public static Customer ToModel(this Entities.Customer entity) => + public static Customer ToModel(this Shop.Customers.Entities.Customer entity) => new() { Id = entity.Id, @@ -118,7 +128,7 @@ public static class EntityModeMappers Whatsapp = entity.Whatsapp }; - public static Lead ToModel(this Entities.Lead entity) => + public static Lead ToModel(this Shop.Leads.Entities.Lead entity) => new() { Id = entity.Id, @@ -136,10 +146,10 @@ 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) => + public static Order ToModel(this Shop.Orders.Entities.Order entity) => new() { Id = entity.Id, @@ -147,35 +157,35 @@ public static class EntityModeMappers UpdatedAt = entity.UpdatedAt, CustomerId = entity.CustomerId, Notes = entity.Notes, - RefundId = entity.RefundId, - QuoteId = entity.QuoteId, Status = entity.Status, - ShoppingCartId = entity.ShoppingCartId, - DepositRequired = entity.DepositRequired, Requirements = entity.Requirements, - Terms = entity.Terms + Terms = entity.Terms, + InvoiceUrl = entity.InvoiceUrl }; - public static OrderRefund ToModel(this Entities.OrderRefund entity) => + public static OrderRefund ToModel(this Shop.Orders.Entities.OrderRefund entity) => new() { Id = entity.Id, CreatedAt = entity.CreatedAt, OrderId = entity.OrderId, Reason = entity.Reason, - Amount = entity.Amount + Amount = entity.Amount }; - public static Product ToModel(this Entities.Product entity) => + public static Product ToModel(this Shop.Products.Entities.Product entity) => new() { Id = entity.Id, Name = entity.Name, Description = entity.Description, - Active = entity.Active + Active = entity.Active, + Summary = entity.Summary, + ImageUrl = entity.ImageUrl, + Thumbnails = entity.Thumbnails }; - public static ProductPrice ToModel(this Entities.ProductPrice entity) => + public static ProductPrice ToModel(this Shop.Products.Entities.ProductPrice entity) => new() { Id = entity.Id, @@ -184,6 +194,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/Extensions/HealthChecks.cs b/LiteCharms.Features/Extensions/HealthChecks.cs index 01bb1ae..ebabd8f 100644 --- a/LiteCharms.Features/Extensions/HealthChecks.cs +++ b/LiteCharms.Features/Extensions/HealthChecks.cs @@ -1,6 +1,6 @@ -using LiteCharms.Infrastructure.HealthChecks; +using LiteCharms.Features.HealthChecks; -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class HealthChecks { diff --git a/LiteCharms.Features/Extensions/Monitoring.cs b/LiteCharms.Features/Extensions/Monitoring.cs index a5b52da..8e5db29 100644 --- a/LiteCharms.Features/Extensions/Monitoring.cs +++ b/LiteCharms.Features/Extensions/Monitoring.cs @@ -1,4 +1,4 @@ -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class Monitoring { diff --git a/LiteCharms.Features/Extensions/Postgres.cs b/LiteCharms.Features/Extensions/Postgres.cs index 50c420c..0e70287 100644 --- a/LiteCharms.Features/Extensions/Postgres.cs +++ b/LiteCharms.Features/Extensions/Postgres.cs @@ -1,6 +1,6 @@ -using LiteCharms.Infrastructure.Database; +using LiteCharms.Features.Shop.Postgres; -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class Postgres { diff --git a/LiteCharms.Features/Extensions/Quartz.cs b/LiteCharms.Features/Extensions/Quartz.cs index a92d462..e22b1ca 100644 --- a/LiteCharms.Features/Extensions/Quartz.cs +++ b/LiteCharms.Features/Extensions/Quartz.cs @@ -1,7 +1,7 @@ -using LiteCharms.Abstractions; -using LiteCharms.Infrastructure.Quartz; +using LiteCharms.Features.Quartz; +using LiteCharms.Features.Quartz.Abstractions; -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class Quartz { diff --git a/LiteCharms.Features/Extensions/ServiceBus.cs b/LiteCharms.Features/Extensions/ServiceBus.cs index ab8d7c0..3a57832 100644 --- a/LiteCharms.Features/Extensions/ServiceBus.cs +++ b/LiteCharms.Features/Extensions/ServiceBus.cs @@ -1,9 +1,9 @@ -using LiteCharms.Abstractions; -using LiteCharms.Infrastructure.ServiceBus; -using LiteCharms.Infrastructure.ServiceBus.Exchanges; -using LiteCharms.Infrastructure.ServiceBus.Queues; +using LiteCharms.Features.ServiceBus; +using LiteCharms.Features.ServiceBus.Abstractions; +using LiteCharms.Features.ServiceBus.Exchanges; +using LiteCharms.Features.ServiceBus.Queues; -namespace LiteCharms.Extensions; +namespace LiteCharms.Features.Extensions; public static class ServiceBus { diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index 3d6552b..9ad7508 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -130,6 +130,7 @@ + -- 2.47.3 From 134d8429c00e8a3f91bab7676b5ce408ce84c31a Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Thu, 14 May 2026 01:33:21 +0200 Subject: [PATCH 3/5] Completed refactor --- LiteCharms.Features.Tests/CommonFixture.cs | 1 + .../NotificationsFeatureTests.cs | 32 +- LiteCharms.Features/Email/EmailService.cs | 8 +- .../SendShopEmailEnquiryEventHandler.cs | 35 +- LiteCharms.Features/Email/IEmailService.cs | 15 - .../Setup.cs => Extensions/Email.cs} | 9 +- .../Extensions/EntityModeMappers.cs | 30 +- LiteCharms.Features/Extensions/Shop.cs | 27 ++ LiteCharms.Features/Models/DateRange.cs | 10 + .../Commands/AddPackageItemsCommand.cs | 25 -- .../Commands/CreatePackageCommand.cs | 23 -- .../Commands/DeletePackageCommand.cs | 25 -- .../Commands/DeletePackageItemsCommand.cs | 16 - .../Handlers/AddPackageItemCommandHandler.cs | 38 --- .../Handlers/CreatePackageCommandHandler.cs | 32 -- .../DeletePackageItemCommandHandler.cs | 32 -- .../DeletePackageItemsCommandHandler.cs | 29 -- .../Handlers/UpdatePackageCommandHandler.cs | 33 -- .../UpdatePackageStatusCommandHandler.cs | 29 -- .../Commands/UpdatePackageCommand.cs | 28 -- .../Commands/UpdatePackageStatusCommand.cs | 22 -- .../Shop/CartPackages/PackageService.cs | 243 ++++++++++++++ .../Queries/GetPackageItemsQuery.cs | 18 -- .../CartPackages/Queries/GetPackageQuery.cs | 18 -- .../CartPackages/Queries/GetPackagesQuery.cs | 33 -- .../Handlers/GetPackageItemsQueryHandler.cs | 32 -- .../Handlers/GetPackageQueryHandler.cs | 27 -- .../Handlers/GetPackagesQueryHandler.cs | 35 -- .../Commands/CreateCustomerCommand.cs | 64 ---- .../Handlers/CreateCustomerCommandHandler.cs | 48 --- .../Handlers/UpdateCustomerCommandHandler.cs | 44 --- .../Commands/UpdateCustomerCommand.cs | 70 ---- .../Shop/Customers/CustomerService.cs | 132 ++++++++ .../Shop/Customers/Models/Records.cs | 73 +++++ .../Customers/Queries/GetCustomerQuery.cs | 18 -- .../Customers/Queries/GetCustomersQuery.cs | 30 -- .../Handlers/GetCustomerQueryHandler.cs | 26 -- .../Handlers/GetCustomersQueryHandler.cs | 33 -- .../Shop/Leads/Commands/CreateLeadCommand.cs | 55 ---- .../Handlers/CreateLeadCommandHandler.cs | 47 --- .../Handlers/UpdateLeadCommandHandler.cs | 29 -- .../Shop/Leads/Commands/UpdateLeadCommand.cs | 28 -- LiteCharms.Features/Shop/Leads/LeadService.cs | 116 +++++++ .../Shop/Leads/Models/Records.cs | 28 ++ .../Leads/Queries/GetCustomerLeadsQuery.cs | 30 -- .../Shop/Leads/Queries/GetLeadsQuery.cs | 30 -- .../Handlers/GetCustomerLeadsQueryHandler.cs | 33 -- .../Queries/Handlers/GetLeadsQueryHandler.cs | 33 -- .../Commands/CreateNotificationCommand.cs | 73 ----- .../CreateNotificationCommandHandler.cs | 40 --- .../UpdateNotificationCommandHandler.cs | 35 -- .../Commands/UpdateNotificationCommand.cs | 28 -- .../Entities/NotificationConfiguration.cs | 4 +- .../ProcessEmailNotificationsEventHandler.cs | 64 +++- .../Events/ProcessEmailNotificationsEvent.cs | 2 +- .../Notifications/INotificationService.cs | 9 - .../Shop/Notifications/Models/Notification.cs | 4 +- .../Shop/Notifications/Models/Records.cs | 20 +- .../Shop/Notifications/NotificationService.cs | 114 ++++++- .../Queries/GetNotificationQuery.cs | 18 -- .../Queries/GetNotificationsQuery.cs | 30 -- .../Handlers/GetNotificationQueryHandler.cs | 26 -- .../Handlers/GetNotificationsQueryHandler.cs | 33 -- .../Orders/Commands/CreateOrderCommand.cs | 40 --- .../Handlers/CreateOrderCommandHandler.cs | 46 --- .../UpdateOrderStatusCommandHandler.cs | 29 -- .../Commands/UpdateOrderStatusCommand.cs | 31 -- .../Shop/Orders/Models/Records.cs | 40 +++ .../Shop/Orders/OrderService.cs | 260 +++++++++++++++ .../Orders/Queries/GetCustomerOrdersQuery.cs | 18 -- .../Orders/Queries/GetOrderRefundQuery.cs | 27 -- .../Shop/Orders/Queries/GetOrdersQuery.cs | 30 -- .../Handlers/GetCustomerOrdersQueryHandler.cs | 32 -- .../Handlers/GetOrderRefundQueryHandler.cs | 27 -- .../Queries/Handlers/GetOrdersQueryHandler.cs | 34 -- .../Handlers/RefundCustomerCommandHandler.cs | 39 --- .../UpdateOrderRefundCommandHandler.cs | 31 -- .../Refunds/Commands/RefundCustomerCommand.cs | 38 --- .../Commands/UpdateOrderRefundCommand.cs | 28 -- .../Queries/GetCustomerRefundsQuery.cs | 18 -- .../Orders/Refunds/Queries/GetRefundQuery.cs | 18 -- .../GetCustomerRefundsQueryHandler.cs | 32 -- .../Queries/Handlers/GetRefundQueryHandler.cs | 27 -- .../Shop/Products/Models/Records.cs | 14 + .../Shop/Products/ProductService.cs | 229 ++++++++++++++ .../Products/Queries/GetProductPriceQuery.cs | 18 -- .../Products/Queries/GetProductPricesQuery.cs | 18 -- .../Shop/Products/Queries/GetProductQuery.cs | 18 -- .../Shop/Products/Queries/GetProductsQuery.cs | 18 -- .../Handlers/GetProductPriceQueryHandler.cs | 32 -- .../Handlers/GetProductPricesQueryHandler.cs | 27 -- .../Handlers/GetProductQueryHandler.cs | 26 -- .../Handlers/GetProductsQueryHandler.cs | 27 -- .../Commands/AssignQuoteToOrderCommand.cs | 25 -- .../AssignQuoteToShoppingCartCommand.cs | 25 -- .../AssignQuoteToOrderCommandHandler.cs | 35 -- ...AssignQuoteToShoppingCartCommandHandler.cs | 32 -- .../UpdateQuoteStatusCommandHandler.cs | 29 -- .../Commands/UpdateQuoteStatusCommand.cs | 25 -- .../Quotes/Queries/GetCustomerQuotesQuery.cs | 18 -- .../Shop/Quotes/Queries/GetQuoteQuery.cs | 18 -- .../Shop/Quotes/Queries/GetQuotesQuery.cs | 30 -- .../Handlers/GetCustomerQuotesQueryHandler.cs | 31 -- .../Queries/Handlers/GetQuoteQueryHandler.cs | 26 -- .../Queries/Handlers/GetQuotesHandler.cs | 33 -- .../Shop/Quotes/QuoteService.cs | 154 +++++++++ .../Commands/AddItemToShoppingCartCommand.cs | 30 -- .../AddPackageToShoppingCartCommand.cs | 25 -- .../Commands/CreateShoppingCartCommand.cs | 25 -- .../Commands/EmptyShoppingCartCommand.cs | 16 - .../AddItemToShoppingCartCommandHandler.cs | 40 --- .../AddPackageToShoppingCartCommandHandler.cs | 39 --- .../CreateShoppingCartCommandHandler.cs | 32 -- .../EmptyShoppingCartCommandHandler.cs | 32 -- ...vePackageFromShoppingCartCommandHandler.cs | 35 -- .../RemoveShoppingCartItemCommandHandler.cs | 36 --- .../UpdateShoppingCartItemCommandHandler.cs | 32 -- .../RemovePackageFromShoppingCartCommand.cs | 25 -- .../Commands/RemoveShoppingCartItemCommand.cs | 25 -- .../Commands/UpdateShoppingCartItemCommand.cs | 30 -- .../Queries/GetCustomerShoppingCartsQuery.cs | 18 -- .../Queries/GetShoppingCartItemsQuery.cs | 18 -- .../Queries/GetShoppingCartPackagesQuery.cs | 18 -- .../Queries/GetShoppingCartQuery.cs | 18 -- .../GetCustomerShoppingCartsQueryHandler.cs | 30 -- .../GetShoppingCartItemsQueryHandler.cs | 30 -- .../GetShoppingCartPackagesQueryHandler.cs | 32 -- .../Handlers/GetShoppingCartQueryHandler.cs | 26 -- .../Shop/ShoppingCarts/ShoppingCartService.cs | 298 ++++++++++++++++++ 129 files changed, 1870 insertions(+), 3165 deletions(-) delete mode 100644 LiteCharms.Features/Email/IEmailService.cs rename LiteCharms.Features/{Email/Extensions/Setup.cs => Extensions/Email.cs} (69%) create mode 100644 LiteCharms.Features/Extensions/Shop.cs create mode 100644 LiteCharms.Features/Models/DateRange.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs create mode 100644 LiteCharms.Features/Shop/CartPackages/PackageService.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Commands/CreateCustomerCommand.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs create mode 100644 LiteCharms.Features/Shop/Customers/CustomerService.cs create mode 100644 LiteCharms.Features/Shop/Customers/Models/Records.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs create mode 100644 LiteCharms.Features/Shop/Leads/LeadService.cs create mode 100644 LiteCharms.Features/Shop/Leads/Models/Records.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Commands/UpdateNotificationCommand.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/INotificationService.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs create mode 100644 LiteCharms.Features/Shop/Orders/Models/Records.cs create mode 100644 LiteCharms.Features/Shop/Orders/OrderService.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs create mode 100644 LiteCharms.Features/Shop/Products/Models/Records.cs create mode 100644 LiteCharms.Features/Shop/Products/ProductService.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs create mode 100644 LiteCharms.Features/Shop/Quotes/QuoteService.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs delete mode 100644 LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs create mode 100644 LiteCharms.Features/Shop/ShoppingCarts/ShoppingCartService.cs diff --git a/LiteCharms.Features.Tests/CommonFixture.cs b/LiteCharms.Features.Tests/CommonFixture.cs index 358aaf2..a73bed2 100644 --- a/LiteCharms.Features.Tests/CommonFixture.cs +++ b/LiteCharms.Features.Tests/CommonFixture.cs @@ -22,6 +22,7 @@ public class CommonFixture : IDisposable Services = new ServiceCollection() .AddMediator() .AddLogging() + .AddShopServices() .AddEmailServiceBus() .AddShopDatabase(Configuration) .AddEmailServices(Configuration) diff --git a/LiteCharms.Features.Tests/NotificationsFeatureTests.cs b/LiteCharms.Features.Tests/NotificationsFeatureTests.cs index 53b8154..ede7fd8 100644 --- a/LiteCharms.Features.Tests/NotificationsFeatureTests.cs +++ b/LiteCharms.Features.Tests/NotificationsFeatureTests.cs @@ -1,19 +1,35 @@ -using LiteCharms.Features.Notifications.Commands; +using LiteCharms.Features.Shop.Notifications; namespace LiteCharms.Features.Tests; -public class NotificationsFeatureTests(CommonFixture fixture) : IClassFixture +public class NotificationsFeatureTests(CommonFixture fixture, ITestOutputHelper output) : IClassFixture { + private readonly NotificationService notificationService = fixture.Services.GetRequiredService(); + [Fact] public async Task CreateNotificationCommand_ShouldSucceed() { - var command = CreateNotification.Create(Models.NotificationDirection.Outgoing, "UnitTest", "khwezi@mngoma.co.za", - "CreateNotificationCommand_ShouldSucceed Test", "Test Message", Models.NotificationPlatforms.Email, Models.Priorities.Medium, - "Khngisa Shop - Test", "shop@litecharms.co.za", Guid.NewGuid().ToString(), Models.CorrelationIdTypes.None, - true, false); + Shop.Notifications.Models.CreateNotification request = new() + { + CorrelationId = Guid.CreateVersion7().ToString(), + CorrelationIdType = Shop.CorrelationIdTypes.None, + Direction = Shop.NotificationDirection.Outgoing, + Platform = Shop.NotificationPlatforms.Email, + Priority = Shop.Priorities.Medium, + Sender = "xUnit Test", + SenderAddress = "khwezi@mngoma.africa", + Recipient = $"{Email.Extensions.Constants.ShopEmailFromName} [Test]", + RecipientAddress = Email.Extensions.Constants.ShopEmailFromAddress, + Subject = "Test Message", + Message = "This is an automation test", + IsHtml = false, + IsInternal = true, + }; - var result = await fixture.Mediator.Send(command); + var createResult = await notificationService.CreateNotificationAsync(request); - Assert.True(result.IsSuccess); + Assert.True(createResult.IsSuccess); + + foreach (var error in createResult.Errors) output.WriteLine(error.Message); } } diff --git a/LiteCharms.Features/Email/EmailService.cs b/LiteCharms.Features/Email/EmailService.cs index 8cb3a82..3e6c103 100644 --- a/LiteCharms.Features/Email/EmailService.cs +++ b/LiteCharms.Features/Email/EmailService.cs @@ -5,7 +5,7 @@ using LiteCharms.Features.Shop; namespace LiteCharms.Features.Email; -public class EmailService(IOptions options) : IEmailService +public class EmailService(IOptions options) : IDisposable { private readonly SmtpSettings settings = options.Value; private readonly SmtpClient client = new(); @@ -14,7 +14,7 @@ public class EmailService(IOptions options) : IEmailService public EmailStatuses Status { get; private set; } = EmailStatuses.Disconnected; - public async Task> SendEmailAsync(Message message, CancellationToken cancellationToken = default) + public async ValueTask> SendEmailAsync(Message message, CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Send"); activity?.SetTag("email.recipient", message.Recipient?.Address); @@ -100,7 +100,7 @@ public class EmailService(IOptions options) : IEmailService } } - public async Task> ConnectAsync(CancellationToken cancellationToken = default) + public async ValueTask> ConnectAsync(CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Connect"); activity?.SetTag("email.smtp.connect", settings.Host); @@ -153,7 +153,7 @@ public class EmailService(IOptions options) : IEmailService } } - public async Task DisconnectAsync(CancellationToken cancellationToken = default) + public async ValueTask DisconnectAsync(CancellationToken cancellationToken = default) { using var activity = EmailTelemetry.Source.StartActivity("Email Disconnect"); activity?.SetTag("email.smtp.disconnect", settings.Host); diff --git a/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs b/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs index f3e703f..f4173a6 100644 --- a/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs +++ b/LiteCharms.Features/Email/Events/Handlers/SendShopEmailEnquiryEventHandler.cs @@ -1,22 +1,27 @@ -using LiteCharms.Features.Notifications.Commands; -using LiteCharms.Features.Shop; +using LiteCharms.Features.Shop; +using LiteCharms.Features.Shop.Notifications; using static LiteCharms.Features.Email.Extensions.Constants; namespace LiteCharms.Features.Email.Events.Handlers; -// TODO: Inject the INotificationService -public class SendShopEmailEnquiryEventHandler(ISender mediator) : +public class SendShopEmailEnquiryEventHandler(NotificationService notificationService) : INotificationHandler { - public async ValueTask Handle(SendShopEmailEnquiryEvent notification, CancellationToken cancellationToken) - { - // TODO: Refactor this to use the NotificationService - var command = CreateNotification.Create(NotificationDirection.Outgoing, notification.SenderName!, - notification.SenderAddress!, notification.Subject!, notification.Message!, NotificationPlatforms.Email, - notification.Priority, ShopEmailFromName, ShopEmailFromAddress, Guid.CreateVersion7().ToString(), - CorrelationIdTypes.None, isInternal: true, isHtml: false); - - // TODO: Remove, deprecated - await mediator.Send(command, cancellationToken); - } + public async ValueTask Handle(SendShopEmailEnquiryEvent notification, CancellationToken cancellationToken) => + await notificationService.CreateNotificationAsync(new Shop.Notifications.Models.CreateNotification + { + CorrelationId = notification.CorrelationId, + CorrelationIdType = CorrelationIdTypes.None, + Direction = NotificationDirection.Outgoing, + IsHtml = false, + IsInternal = true, + Message = notification.Message, + Platform = NotificationPlatforms.Email, + Priority = notification.Priority, + Subject = notification.Subject!, + Sender = notification.SenderName!, + SenderAddress = notification.SenderAddress!, + Recipient = ShopEmailFromName, + RecipientAddress = ShopEmailFromAddress + }, cancellationToken); } diff --git a/LiteCharms.Features/Email/IEmailService.cs b/LiteCharms.Features/Email/IEmailService.cs deleted file mode 100644 index bda6a6f..0000000 --- a/LiteCharms.Features/Email/IEmailService.cs +++ /dev/null @@ -1,15 +0,0 @@ -using LiteCharms.Features.Email.Models; -using LiteCharms.Features.Shop; - -namespace LiteCharms.Features.Email; - -public interface IEmailService : IDisposable -{ - EmailStatuses Status { get; } - - Task> SendEmailAsync(Message message, CancellationToken cancellationToken = default); - - Task> ConnectAsync(CancellationToken cancellationToken = default); - - Task DisconnectAsync(CancellationToken cancellationToken = default); -} diff --git a/LiteCharms.Features/Email/Extensions/Setup.cs b/LiteCharms.Features/Extensions/Email.cs similarity index 69% rename from LiteCharms.Features/Email/Extensions/Setup.cs rename to LiteCharms.Features/Extensions/Email.cs index f937dda..ae3756d 100644 --- a/LiteCharms.Features/Email/Extensions/Setup.cs +++ b/LiteCharms.Features/Extensions/Email.cs @@ -1,14 +1,15 @@ -using LiteCharms.Features.Email.Configuration; +using LiteCharms.Features.Email; +using LiteCharms.Features.Email.Configuration; -namespace LiteCharms.Features.Email.Extensions; +namespace LiteCharms.Features.Extensions; -public static class Setup +public static class Email { public static IServiceCollection AddEmailServices(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration.GetSection("Email")); - services.AddSingleton(); + services.AddSingleton(); services.AddOpenTelemetry() .WithTracing(tracing => tracing.AddSource("LiteCharms.EmailService")) diff --git a/LiteCharms.Features/Extensions/EntityModeMappers.cs b/LiteCharms.Features/Extensions/EntityModeMappers.cs index baeb69c..d73a028 100644 --- a/LiteCharms.Features/Extensions/EntityModeMappers.cs +++ b/LiteCharms.Features/Extensions/EntityModeMappers.cs @@ -11,7 +11,7 @@ namespace LiteCharms.Features.Extensions; public static class EntityModeMappers { - public static ShoppingCartPackage ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartPackage entity) => + public static ShoppingCartPackage ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage entity) => new() { Id = entity.Id, @@ -20,7 +20,7 @@ public static class EntityModeMappers ShoppingCartId = entity.ShoppingCartId }; - public static PackageItem ToModel(this Shop.CartPackages.Entities.PackageItem entity) => + public static PackageItem ToModel(this Features.Shop.CartPackages.Entities.PackageItem entity) => new() { Id = entity.Id, @@ -30,7 +30,7 @@ public static class EntityModeMappers ProductPriceId = entity.ProductPriceId }; - public static Package ToModel(this Shop.CartPackages.Entities.Package entity) => + public static Package ToModel(this Features.Shop.CartPackages.Entities.Package entity) => new() { Id = entity.Id, @@ -43,7 +43,7 @@ public static class EntityModeMappers Summary = entity.Summary }; - public static ShoppingCartItem ToModel(this Shop.ShoppingCarts.Entities.ShoppingCartItem entity) => + public static ShoppingCartItem ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCartItem entity) => new() { Id = entity.Id, @@ -54,7 +54,7 @@ public static class EntityModeMappers ShoppingCartId = entity.ShoppingCartId }; - public static ShoppingCart ToModel(this Shop.ShoppingCarts.Entities.ShoppingCart entity) => + public static ShoppingCart ToModel(this Features.Shop.ShoppingCarts.Entities.ShoppingCart entity) => new() { Id = entity.Id, @@ -64,7 +64,7 @@ public static class EntityModeMappers OrderId = entity.OrderId }; - public static Quote ToModel(this Shop.Quotes.Entities.Quote entity) => + public static Quote ToModel(this Features.Shop.Quotes.Entities.Quote entity) => new() { Id = entity.Id, @@ -79,7 +79,7 @@ public static class EntityModeMappers OrderId = entity.OrderId }; - public static Notification ToModel(this Shop.Notifications.Entities.Notification entity) => + public static Notification ToModel(this Features.Shop.Notifications.Entities.Notification entity) => new() { Id = entity.Id, @@ -89,9 +89,9 @@ public static class EntityModeMappers CorrelationId = entity.CorrelationId, CorrelationIdType = entity.CorrelationIdType, IsInternal = entity.IsInternal, - Sender = entity.Sender, + SenderAddress = entity.SenderAddress, Platform = entity.Platform, - Recipient = entity.Recipient, + RecipientName = entity.RecipientName, Subject = entity.Subject, Processed = entity.Processed, SenderName = entity.SenderName, @@ -103,7 +103,7 @@ public static class EntityModeMappers Errors = entity.Errors }; - public static Customer ToModel(this Shop.Customers.Entities.Customer entity) => + public static Customer ToModel(this Features.Shop.Customers.Entities.Customer entity) => new() { Id = entity.Id, @@ -128,7 +128,7 @@ public static class EntityModeMappers Whatsapp = entity.Whatsapp }; - public static Lead ToModel(this Shop.Leads.Entities.Lead entity) => + public static Lead ToModel(this Features.Shop.Leads.Entities.Lead entity) => new() { Id = entity.Id, @@ -149,7 +149,7 @@ public static class EntityModeMappers Status = entity.Status }; - public static Order ToModel(this Shop.Orders.Entities.Order entity) => + public static Order ToModel(this Features.Shop.Orders.Entities.Order entity) => new() { Id = entity.Id, @@ -163,7 +163,7 @@ public static class EntityModeMappers InvoiceUrl = entity.InvoiceUrl }; - public static OrderRefund ToModel(this Shop.Orders.Entities.OrderRefund entity) => + public static OrderRefund ToModel(this Features.Shop.Orders.Entities.OrderRefund entity) => new() { Id = entity.Id, @@ -173,7 +173,7 @@ public static class EntityModeMappers Amount = entity.Amount }; - public static Product ToModel(this Shop.Products.Entities.Product entity) => + public static Product ToModel(this Features.Shop.Products.Entities.Product entity) => new() { Id = entity.Id, @@ -185,7 +185,7 @@ public static class EntityModeMappers Thumbnails = entity.Thumbnails }; - public static ProductPrice ToModel(this Shop.Products.Entities.ProductPrice entity) => + public static ProductPrice ToModel(this Features.Shop.Products.Entities.ProductPrice entity) => new() { Id = entity.Id, diff --git a/LiteCharms.Features/Extensions/Shop.cs b/LiteCharms.Features/Extensions/Shop.cs new file mode 100644 index 0000000..78502de --- /dev/null +++ b/LiteCharms.Features/Extensions/Shop.cs @@ -0,0 +1,27 @@ +using LiteCharms.Features.Shop.CartPackages; +using LiteCharms.Features.Shop.Customers; +using LiteCharms.Features.Shop.Leads; +using LiteCharms.Features.Shop.Notifications; +using LiteCharms.Features.Shop.Orders; +using LiteCharms.Features.Shop.Products; +using LiteCharms.Features.Shop.Quotes; +using LiteCharms.Features.Shop.ShoppingCarts; + +namespace LiteCharms.Features.Extensions; + +public static class Shop +{ + public static IServiceCollection AddShopServices(this IServiceCollection services) + { + services.AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + + return services; + } +} diff --git a/LiteCharms.Features/Models/DateRange.cs b/LiteCharms.Features/Models/DateRange.cs new file mode 100644 index 0000000..c82cba3 --- /dev/null +++ b/LiteCharms.Features/Models/DateRange.cs @@ -0,0 +1,10 @@ +namespace LiteCharms.Features.Models; + +public class DateRange +{ + public DateOnly From { get; set; } + + public DateOnly To { get; set; } + + public int MaxRecords { get; set; } +} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs deleted file mode 100644 index be87a47..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/AddPackageItemsCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class AddPackageItemCommand : IRequest> -{ - public Guid PackageId { get; set; } - - public Guid ProductPriceId { get; set; } - - private AddPackageItemCommand(Guid packageId, Guid productPriceId) - { - PackageId = packageId; - ProductPriceId = productPriceId; - } - - public static AddPackageItemCommand Create(Guid packageId, Guid productPriceId) - { - if (packageId == Guid.Empty) - throw new ArgumentException("Package id is required", nameof(packageId)); - - if (productPriceId == Guid.Empty) - throw new ArgumentException("Product price id is required", nameof(productPriceId)); - - return new(packageId, productPriceId); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs deleted file mode 100644 index 4a86846..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/CreatePackageCommand.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class CreatePackageCommand : IRequest> -{ - public string? Name { get; set; } - - public string? Description { get; set; } - - private CreatePackageCommand(string? name, string? description) - { - Name = name; - Description = description; - } - - public static CreatePackageCommand Create(string? name, string? description) - { - ArgumentException.ThrowIfNullOrWhiteSpace(name, nameof(name)); - - ArgumentException.ThrowIfNullOrWhiteSpace(description, nameof(description)); - - return new(name, description); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs deleted file mode 100644 index 5957dce..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class DeletePackageItemCommand : IRequest -{ - public Guid PackageId { get; set; } - - public Guid PackageItemId { get; set; } - - private DeletePackageItemCommand(Guid packageId, Guid packageItemId) - { - PackageId = packageId; - PackageItemId = packageItemId; - } - - public static DeletePackageItemCommand Create(Guid packageId, Guid packageItemId) - { - if (packageId == Guid.Empty) - throw new ArgumentException("Package id is required", nameof(packageId)); - - if (packageItemId == Guid.Empty) - throw new ArgumentException("Product price id is required", nameof(packageItemId)); - - return new(packageId, packageItemId); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs deleted file mode 100644 index c9aa3e0..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/DeletePackageItemsCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class DeletePackageItemsCommand : IRequest -{ - public Guid PackageId { get; set; } - - private DeletePackageItemsCommand(Guid packageId) => PackageId = packageId; - - public static DeletePackageItemsCommand Create(Guid packageId) - { - if (packageId == Guid.Empty) - throw new ArgumentException("Package ID is required", nameof(packageId)); - - return new(packageId); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs deleted file mode 100644 index 71c54f7..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/AddPackageItemCommandHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class AddPackageItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(AddPackageItemCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken)) - return Result.Fail($"Could not find package by ID {request.PackageId}"); - - if (!await context.ProductPrices.AnyAsync(p => p.Id == request.ProductPriceId && p.Active == true, cancellationToken)) - return Result.Fail($"Could not find an active product price by ID {request.ProductPriceId}"); - - if (await context.PackageItems.AnyAsync(p => p.ProductPriceId == request.ProductPriceId && p.PackageId == request.PackageId, cancellationToken)) - return Result.Fail($"Product price {request.ProductPriceId} is already added to this package {request.PackageId}"); - - var newPackageItem = context.PackageItems.Add(new Entities.PackageItem - { - PackageId = request.PackageId, - ProductPriceId = request.ProductPriceId, - Active = true - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(newPackageItem.Entity.Id) - : Result.Fail($"Failed to add new package item by ID {request.ProductPriceId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs deleted file mode 100644 index ff7847e..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/CreatePackageCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class CreatePackageCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(CreatePackageCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (await context.Packages.AnyAsync(p => p.Name == request.Name, cancellationToken)) - return Result.Fail($"A package by the same name already exists: {request.Name}"); - - var newPackage = context.Packages.Add(new Entities.Package - { - Name = request.Name, - Description = request.Description, - Active = true - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(newPackage.Entity.Id) - : Result.Fail($"Failed to create a new package by the name: {request.Name}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs deleted file mode 100644 index 7d7284e..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class DeletePackageItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(DeletePackageItemCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken)) - return Result.Fail($"Could not find package by ID {request.PackageId}"); - - var item = await context.PackageItems.FirstOrDefaultAsync(p => p.Id == request.PackageItemId && p.PackageId == request.PackageId, cancellationToken); - - if(item is null) - return Result.Fail($"Product item {request.PackageItemId} is already added to this package {request.PackageId}"); - - context.PackageItems.Remove(item); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to delete package item by id {request.PackageItemId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs deleted file mode 100644 index bad0e89..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/DeletePackageItemsCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class DeletePackageItemsCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(DeletePackageItemsCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken)) - return Result.Fail($"Could not find package by ID {request.PackageId}"); - - var items = await context.PackageItems.Where(i => i.PackageId == request.PackageId).ToArrayAsync(cancellationToken); - - context.PackageItems.RemoveRange(items); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to delete package {request.PackageId} items"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs deleted file mode 100644 index 15945ff..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageCommandHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class UpdatePackageCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdatePackageCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (await context.Packages.AnyAsync(p => p.Name == request.Name, cancellationToken)) - return Result.Fail($"A package by the same name already exists: {request.Name}"); - - var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken); - - if (package is null) - return Result.Fail($"Could not find package by id {request.PackageId}"); - - package.Name = request.Name; - package.Description = request.Description; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to update package with id {request.PackageId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs b/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs deleted file mode 100644 index 16c6482..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/Handlers/UpdatePackageStatusCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Commands.Handlers; - -public class UpdatePackageStatusCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdatePackageStatusCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken); - - if (package is null) - return Result.Fail($"Could not find package by id {request.PackageId}"); - - package.Active = request.Active; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to update package with id {request.PackageId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs deleted file mode 100644 index 938ca44..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageCommand.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class UpdatePackageCommand : IRequest -{ - public Guid PackageId { get; set; } - - public string? Name { get; set; } - - public string? Description { get; set; } - - private UpdatePackageCommand(Guid packageId, string? name, string? description) - { - PackageId = packageId; - Name = name; - Description = description; - } - - public static UpdatePackageCommand Create(Guid packageId, string? name, string? description) - { - if (packageId == Guid.Empty) - throw new ArgumentException($"Package ID is required", nameof(packageId)); - - ArgumentNullException.ThrowIfNullOrWhiteSpace(name, nameof(name)); - ArgumentNullException.ThrowIfNullOrWhiteSpace(description, nameof(description)); - - return new(packageId, name, description); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs b/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs deleted file mode 100644 index 7be4651..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Commands/UpdatePackageStatusCommand.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace LiteCharms.Features.CartPackages.Commands; - -public class UpdatePackageStatusCommand : IRequest -{ - public Guid PackageId { get; set; } - - public bool Active { get; set; } - - private UpdatePackageStatusCommand(Guid packageId, bool active) - { - PackageId = packageId; - Active = active; - } - - public static UpdatePackageStatusCommand Create(Guid packageId, bool active) - { - if(packageId == Guid.Empty) - throw new ArgumentException($"Package id is required", nameof(packageId)); - - return new(packageId, active); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/PackageService.cs b/LiteCharms.Features/Shop/CartPackages/PackageService.cs new file mode 100644 index 0000000..1df6c00 --- /dev/null +++ b/LiteCharms.Features/Shop/CartPackages/PackageService.cs @@ -0,0 +1,243 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.CartPackages.Models; +using LiteCharms.Features.Shop.Postgres; +using static LiteCharms.Features.Extensions.Timezones; + +namespace LiteCharms.Features.Shop.CartPackages; + +public class PackageService(IDbContextFactory contextFactory) +{ + public async ValueTask> AddPackageItemAsync(Guid packageId, Guid productPriceId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) + return Result.Fail($"Could not find package by ID {packageId}"); + + if (!await context.ProductPrices.AnyAsync(p => p.Id == productPriceId && p.Active == true, cancellationToken)) + return Result.Fail($"Could not find an active product price by ID {productPriceId}"); + + if (await context.PackageItems.AnyAsync(p => p.ProductPriceId == productPriceId && p.PackageId == packageId, cancellationToken)) + return Result.Fail($"Product price {productPriceId} is already added to this package {packageId}"); + + var newPackageItem = context.PackageItems.Add(new Entities.PackageItem + { + PackageId = packageId, + ProductPriceId = productPriceId, + Active = true + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newPackageItem.Entity.Id) + : Result.Fail($"Failed to add new package item by ID {productPriceId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreatePackageAsync(string? name, string? summary, string? description, string? ImageUrl, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (await context.Packages.AnyAsync(p => p.Name == name, cancellationToken)) + return Result.Fail($"A package by the same name already exists: {name}"); + + var newPackage = context.Packages.Add(new Entities.Package + { + UpdatedAt = null, + Name = name, + Summary = summary, + Description = description, + ImageUrl = ImageUrl, + Active = true + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newPackage.Entity.Id) + : Result.Fail($"Failed to create a new package by the name: {name}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask DeletePackageItemAsync(Guid packageId, Guid packageItemId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) + return Result.Fail($"Could not find package by ID {packageId}"); + + var item = await context.PackageItems.FirstOrDefaultAsync(p => p.Id == packageItemId && p.PackageId == packageId, cancellationToken); + + if (item is null) + return Result.Fail($"Product item {packageItemId} is already added to this package {packageId}"); + + context.PackageItems.Remove(item); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to delete package item by id {packageItemId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask DeletePackageItemsAsync(Guid packageId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) + return Result.Fail($"Could not find package by ID {packageId}"); + + var items = await context.PackageItems.Where(i => i.PackageId == packageId).ToArrayAsync(cancellationToken); + + context.PackageItems.RemoveRange(items); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to delete package {packageId} items"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetPackageAsync(Guid packageId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == packageId, cancellationToken); + + return package is not null + ? Result.Ok(package.ToModel()) + : Result.Fail($"Failed to find package by ID {packageId}"); + + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetPackageItemsAsync(Guid packageId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) + return Result.Fail($"Package could not be found with ID {packageId}"); + + var items = await context.PackageItems.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(p => p.PackageId == packageId) + .ToArrayAsync(cancellationToken); + + return items?.Length > 0 + ? Result.Ok(items.Select(i => i.ToModel()).ToArray()) + : Result.Fail($"Could not find package items by package ID {packageId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetPackagesAsync(Guid packageId, DateRange range, bool active, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var packages = await context.Packages + .AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(p => p.CreatedAt >= fromDate && p.CreatedAt <= toDate) + .Where(p => p.Active == active) + .Take(range.MaxRecords) + .ToArrayAsync(cancellationToken); + + return packages?.Length > 0 + ? Result.Ok(packages.Select(o => o.ToModel()).ToArray()) + : Result.Fail(new Error($"No packages found for the specified date range {range.From} - {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> UpdatePackageAsync(Guid packageId, string? name, string? summary, string? description, string? ImageUrl, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (await context.Packages.AnyAsync(p => p.Name == name, cancellationToken)) + return Result.Fail($"A package by the same name already exists: {name}"); + + var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == packageId, cancellationToken); + + if (package is null) + return Result.Fail($"Could not find package by id {packageId}"); + + package.Name = name; + package.Summary = summary; + package.Description = description; + package.ImageUrl = ImageUrl; + package.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to update package with id {packageId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> UpdatePackageStatusAsync(Guid packageId, bool active, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == packageId, cancellationToken); + + if (package is null) + return Result.Fail($"Could not find package by id {packageId}"); + + package.Active = active; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to update package with id {packageId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs deleted file mode 100644 index 3315726..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageItemsQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.CartPackages.Models; - -namespace LiteCharms.Features.CartPackages.Queries; - -public class GetPackageItemsQuery : IRequest> -{ - public Guid PackageId { get; set; } - - private GetPackageItemsQuery(Guid packageId) => PackageId = packageId; - - public static GetPackageItemsQuery Create(Guid packageId) - { - if (packageId == Guid.Empty) - throw new ArgumentException("Package ID is required", nameof(packageId)); - - return new(packageId); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs deleted file mode 100644 index a05264e..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackageQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.CartPackages.Models; - -namespace LiteCharms.Features.CartPackages.Queries; - -public class GetPackageQuery : IRequest> -{ - public Guid PackageId { get; set; } - - private GetPackageQuery(Guid packageId) => PackageId = packageId; - - public static GetPackageQuery Create(Guid packageId) - { - if(packageId == Guid.Empty) - throw new ArgumentException("Package ID is required", nameof(packageId)); - - return new(packageId); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs b/LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs deleted file mode 100644 index 036ff09..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/GetPackagesQuery.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Features.Shop.CartPackages.Models; - -namespace LiteCharms.Features.CartPackages.Queries; - -public class GetPackagesQuery : IRequest> -{ - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - public int MaxRecords { get; set; } - - public bool Active { get; set; } - - private GetPackagesQuery(DateOnly from, DateOnly to, int maxRecords = 1000, bool active = true) - { - From = from; - To = to; - MaxRecords = maxRecords; - Active = active; - } - - public static GetPackagesQuery Create(DateOnly from, DateOnly to, int maxRecords = 1000, bool active = true) - { - 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, active); - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs deleted file mode 100644 index 3b19cdc..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageItemsQueryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.CartPackages.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Queries.Handlers; - -public class GetPackageItemsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetPackageItemsQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken)) - return Result.Fail($"Package could not be found with ID {request.PackageId}"); - - var items = await context.PackageItems.AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(p => p.PackageId == request.PackageId) - .ToArrayAsync(cancellationToken); - - return items?.Length > 0 - ? Result.Ok(items.Select(i => i.ToModel()).ToArray()) - : Result.Fail($"Could not find package items by package ID {request.PackageId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs deleted file mode 100644 index d822584..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackageQueryHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.CartPackages.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Queries.Handlers; - -public class GetPackageQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetPackageQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var package = await context.Packages.FirstOrDefaultAsync(p => p.Id == request.PackageId, cancellationToken); - - return package is not null - ? Result.Ok(package.ToModel()) - : Result.Fail($"Failed to find package by ID {request.PackageId}"); - - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs b/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs deleted file mode 100644 index 1ef691f..0000000 --- a/LiteCharms.Features/Shop/CartPackages/Queries/Handlers/GetPackagesQueryHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.CartPackages.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.CartPackages.Queries.Handlers; - -public class GetPackagesQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetPackagesQuery 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 packages = await context.Packages - .AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(p => p.CreatedAt >= fromDate && p.CreatedAt <= toDate) - .Where(p => p.Active == request.Active) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return packages?.Length > 0 - ? Result.Ok(packages.Select(o => o.ToModel()).ToArray()) - : Result.Fail(new Error($"No packages 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/Shop/Customers/Commands/CreateCustomerCommand.cs b/LiteCharms.Features/Shop/Customers/Commands/CreateCustomerCommand.cs deleted file mode 100644 index f0b3560..0000000 --- a/LiteCharms.Features/Shop/Customers/Commands/CreateCustomerCommand.cs +++ /dev/null @@ -1,64 +0,0 @@ -namespace LiteCharms.Features.Customers.Commands; - -public class CreateCustomerCommand : IRequest> -{ - public string? Company { get; set; } - - public string Name { get; set; } - - public string LastName { get; set; } - - public string? Tax { get; set; } - - public string Email { get; set; } - - public string? Discord { get; set; } - - public string? Slack { get; set; } - - public string? LinkedIn { get; set; } - - public string? Whatsapp { get; set; } - - public string? Website { get; set; } - - public string? Phone { get; set; } - - public string? Address { get; set; } - - public string? City { get; set; } - - public string? Region { get; set; } - - public string? Country { get; set; } - - public string? PostalCode { get; set; } - - private CreateCustomerCommand(string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode) - { - Name = name; - LastName = lastName; - Company = company; - Tax = tax; - Email = email; - Discord = discord; - Slack = slack; - LinkedIn = linkedIn; - Whatsapp = whatsapp; - Website = website; - Phone = phone; - Address = address; - City = city; - Region = region; - Country = country; - PostalCode = postalCode; - } - - public static CreateCustomerCommand Create(string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode) - { - if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(lastName) && string.IsNullOrWhiteSpace(email)) - throw new ArgumentException("At the following fields must be provided: Name, LastName, Email"); - - return new(name, lastName, company, tax, email, discord, slack, linkedIn, whatsapp, website, phone, address, city, region, country, postalCode); - } -} diff --git a/LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs deleted file mode 100644 index bf1a5d6..0000000 --- a/LiteCharms.Features/Shop/Customers/Commands/Handlers/CreateCustomerCommandHandler.cs +++ /dev/null @@ -1,48 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Customers.Commands.Handlers; - -public class CreateCustomerCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(CreateCustomerCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var customerEmail = request.Email.ToLower().Trim(); - - if (await context.Customers.AnyAsync(c => c.Email == customerEmail, cancellationToken)) - return Result.Fail(new Error($"A customer with the email {customerEmail} already exists")); - - var newCustomer = context.Customers.Add(new Entities.Customer - { - Company = request.Company, - Name = request.Name, - LastName = request.LastName, - Tax = request.Tax, - Email = customerEmail, - Discord = request.Discord, - Slack = request.Slack, - LinkedIn = request.LinkedIn, - Whatsapp = request.Whatsapp, - Website = request.Website, - Phone = request.Phone, - Address = request.Address, - City = request.City, - Region = request.Region, - Country = request.Country, - PostalCode = request.PostalCode, - Active = true, - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(newCustomer.Entity.Id) - : Result.Fail(new Error($"Failed to create customer {customerEmail}")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs deleted file mode 100644 index 031f591..0000000 --- a/LiteCharms.Features/Shop/Customers/Commands/Handlers/UpdateCustomerCommandHandler.cs +++ /dev/null @@ -1,44 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Customers.Commands.Handlers; - -public class UpdateCustomerCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdateCustomerCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == request.CustomerId, cancellationToken); - - if (customer is null) - return Result.Fail(new Error($"Customer with ID {request.CustomerId} not found.")); - - customer.Name = request.Name; - customer.LastName = request.LastName; - customer.Email = request.Email; - customer.Company = request.Company; - customer.Address = request.Address; - customer.City = request.City; - customer.Region = request.Region; - customer.Country = request.Country; - customer.PostalCode = request.PostalCode; - customer.Phone = request.Phone; - customer.Tax = request.Tax; - customer.City = request.City; - customer.Discord = request.Discord; - customer.Slack = request.Slack; - customer.LinkedIn = request.LinkedIn; - customer.Whatsapp = request.Whatsapp; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail(new Error($"Failed to update the customer {request.CustomerId}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} \ No newline at end of file diff --git a/LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs b/LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs deleted file mode 100644 index ac4f0cd..0000000 --- a/LiteCharms.Features/Shop/Customers/Commands/UpdateCustomerCommand.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace LiteCharms.Features.Customers.Commands; - -public class UpdateCustomerCommand : IRequest -{ - public Guid CustomerId { get; set; } - - public string? Company { get; set; } - - public string? Name { get; set; } - - public string? LastName { get; set; } - - public string? Tax { get; set; } - - public string? Email { get; set; } - - public string? Discord { get; set; } - - public string? Slack { get; set; } - - public string? LinkedIn { get; set; } - - public string? Whatsapp { get; set; } - - public string? Website { get; set; } - - public string? Phone { get; set; } - - public string? Address { get; set; } - - public string? City { get; set; } - - public string? Region { get; set; } - - public string? Country { get; set; } - - public string? PostalCode { get; set; } - - private UpdateCustomerCommand(Guid customerId, string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode) - { - CustomerId = customerId; - Name = name; - LastName = lastName; - Company = company; - Tax = tax; - Email = email; - Discord = discord; - Slack = slack; - LinkedIn = linkedIn; - Whatsapp = whatsapp; - Website = website; - Phone = phone; - Address = address; - City = city; - Region = region; - Country = country; - PostalCode = postalCode; - } - - public static UpdateCustomerCommand Create(Guid customerId, string name, string lastName, string? company, string? tax, string email, string? discord, string? slack, string? linkedIn, string? whatsapp, string? website, string? phone, string? address, string? city, string? region, string? country, string? postalCode) - { - if (customerId == Guid.Empty) - throw new ArgumentException("Customer ID is required.", nameof(customerId)); - - if (string.IsNullOrWhiteSpace(name) && string.IsNullOrWhiteSpace(lastName) && string.IsNullOrWhiteSpace(email)) - throw new ArgumentException("At the following fields must be provided: Name, LastName, Email"); - - return new(customerId, name, lastName, company, tax, email, discord, slack, linkedIn, whatsapp, website, phone, address, city, region, country, postalCode); - } -} diff --git a/LiteCharms.Features/Shop/Customers/CustomerService.cs b/LiteCharms.Features/Shop/Customers/CustomerService.cs new file mode 100644 index 0000000..531ecfe --- /dev/null +++ b/LiteCharms.Features/Shop/Customers/CustomerService.cs @@ -0,0 +1,132 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.Customers.Models; +using LiteCharms.Features.Shop.Postgres; + +namespace LiteCharms.Features.Shop.Customers; + +public class CustomerService(IDbContextFactory contextFactory) +{ + public async ValueTask> CreateCustomerAsync(CreateCustomer request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customerEmail = request.Email.ToLower().Trim(); + + if (await context.Customers.AnyAsync(c => c.Email == customerEmail, cancellationToken)) + return Result.Fail(new Error($"A customer with the email {customerEmail} already exists")); + + var newCustomer = context.Customers.Add(new Entities.Customer + { + Company = request.Company, + Name = request.Name, + LastName = request.LastName, + Tax = request.Tax, + Email = customerEmail, + Discord = request.Discord, + Slack = request.Slack, + LinkedIn = request.LinkedIn, + Whatsapp = request.Whatsapp, + Website = request.Website, + Phone = request.Phone, + Address = request.Address, + City = request.City, + Region = request.Region, + Country = request.Country, + PostalCode = request.PostalCode, + Active = true, + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newCustomer.Entity.Id) + : Result.Fail(new Error($"Failed to create customer {customerEmail}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerAsync(Guid customerId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == customerId, cancellationToken); + + return customer is not null + ? Result.Ok(customer.ToModel()) + : Result.Fail($"Customer not found with id {customerId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomersAsync(DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customers = await context.Customers.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(c => c.CreatedAt >= fromDate && c.CreatedAt <= toDate) + .Take(range.MaxRecords) + .ToArrayAsync(cancellationToken); + + return customers?.Length > 0 + ? Result.Ok(customers.Select(c => c.ToModel()).ToArray()) + : Result.Fail(new Error("No customers found in the specified date range.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateCustomerAsync(UpdateCustomer request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var customer = await context.Customers.FirstOrDefaultAsync(c => c.Id == request.CustomerId, cancellationToken); + + if (customer is null) + return Result.Fail(new Error($"Customer with ID {request.CustomerId} not found.")); + + customer.Name = request.Name; + customer.LastName = request.LastName; + customer.Email = request.Email; + customer.Company = request.Company; + customer.Address = request.Address; + customer.City = request.City; + customer.Region = request.Region; + customer.Country = request.Country; + customer.PostalCode = request.PostalCode; + customer.Phone = request.Phone; + customer.Tax = request.Tax; + customer.City = request.City; + customer.Discord = request.Discord; + customer.Slack = request.Slack; + customer.LinkedIn = request.LinkedIn; + customer.Whatsapp = request.Whatsapp; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to update the customer {request.CustomerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Shop/Customers/Models/Records.cs b/LiteCharms.Features/Shop/Customers/Models/Records.cs new file mode 100644 index 0000000..0b4c788 --- /dev/null +++ b/LiteCharms.Features/Shop/Customers/Models/Records.cs @@ -0,0 +1,73 @@ +namespace LiteCharms.Features.Shop.Customers.Models; + +public record CreateCustomer +{ + public string? Company { get; set; } + + public required string Name { get; set; } + + public required string LastName { get; set; } + + public string? Tax { get; set; } + + public required string Email { get; set; } + + public string? Discord { get; set; } + + public string? Slack { get; set; } + + public string? LinkedIn { get; set; } + + public string? Whatsapp { get; set; } + + public string? Website { get; set; } + + public string? Phone { get; set; } + + public string? Address { get; set; } + + public string? City { get; set; } + + public string? Region { get; set; } + + public string? Country { get; set; } + + public string? PostalCode { get; set; } +} + +public record UpdateCustomer +{ + public required Guid CustomerId { get; set; } + + public string? Company { get; set; } + + public string? Name { get; set; } + + public string? LastName { get; set; } + + public string? Tax { get; set; } + + public string? Email { get; set; } + + public string? Discord { get; set; } + + public string? Slack { get; set; } + + public string? LinkedIn { get; set; } + + public string? Whatsapp { get; set; } + + public string? Website { get; set; } + + public string? Phone { get; set; } + + public string? Address { get; set; } + + public string? City { get; set; } + + public string? Region { get; set; } + + public string? Country { get; set; } + + public string? PostalCode { get; set; } +} \ No newline at end of file diff --git a/LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs b/LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs deleted file mode 100644 index c6ee4b0..0000000 --- a/LiteCharms.Features/Shop/Customers/Queries/GetCustomerQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Customers.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/Shop/Customers/Queries/GetCustomersQuery.cs b/LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs deleted file mode 100644 index 9c830b4..0000000 --- a/LiteCharms.Features/Shop/Customers/Queries/GetCustomersQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Customers.Models; - -namespace LiteCharms.Features.Customers.Queries; - -public class GetCustomersQuery : IRequest> -{ - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - public int MaxRecords { get; set; } - - private GetCustomersQuery(DateOnly from, DateOnly to, int maxRecords = 1000) - { - From = from; - To = to; - MaxRecords = maxRecords; - } - - public static GetCustomersQuery 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/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs deleted file mode 100644 index cbe478d..0000000 --- a/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomerQueryHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Customers.Models; -using LiteCharms.Features.Shop.Postgres; - -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/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs b/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs deleted file mode 100644 index b9d1e99..0000000 --- a/LiteCharms.Features/Shop/Customers/Queries/Handlers/GetCustomersQueryHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Customers.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Customers.Queries.Handlers; - -public class GetCustomersQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetCustomersQuery 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 customers = await context.Customers.AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(c => c.CreatedAt >= fromDate && c.CreatedAt <= toDate) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return customers?.Length > 0 - ? Result.Ok(customers.Select(c => c.ToModel()).ToArray()) - : Result.Fail(new Error("No customers found in the specified date range.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs b/LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs deleted file mode 100644 index 5b120df..0000000 --- a/LiteCharms.Features/Shop/Leads/Commands/CreateLeadCommand.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace LiteCharms.Features.Leads.Commands; - -public class CreateLeadCommand : IRequest> -{ - public Guid? CustomerId { get; set; } - - public string? Source { get; set; } - - public string? ClickId { get; set; } - - public string? WebClickId { get; set; } - - public string? AppClickId { get; set; } - - public long? CampaignId { get; set; } - - public long? AdGroupId { get; set; } - - public long? AdName { get; set; } - - public long? TargetId { get; set; } - - public long? FeedItemId { get; set; } - - public string? ClickLocation { get; set; } - - public string? AttribusionHash { get; set; } - - private CreateLeadCommand(Guid? customerId, string source, string clickId, string webClickId, string appClickId, long? campaignId, long? adGroupId, long? adName, long? targetId, long? feedItemId, string? clickLocation, string? attribusionHash) - { - CustomerId = customerId; - Source = source; - ClickId = clickId; - WebClickId = webClickId; - AppClickId = appClickId; - CampaignId = campaignId; - AdGroupId = adGroupId; - AdName = adName; - TargetId = targetId; - FeedItemId = feedItemId; - ClickLocation = clickLocation; - AttribusionHash = attribusionHash; - } - - public static CreateLeadCommand Create(Guid? customerId, string source, string clickId, string webClickId, string appClickId, long? campaignId, long? adGroupId, long? adName, long? targetId, long? feedItemId, string? clickLocation, string? attribusionHash) - { - if(string.IsNullOrWhiteSpace(source)) - throw new ArgumentNullException("Lead source is required to create a lead.", nameof(source)); - - if (string.IsNullOrWhiteSpace(clickId) || string.IsNullOrWhiteSpace(appClickId) || string.IsNullOrWhiteSpace(webClickId)) - throw new ArgumentException("ClickId, App ClickId and Web ClickId are required to create a lead."); - - return new(customerId, source, clickId, webClickId, appClickId, campaignId, adGroupId, adName, targetId, feedItemId, clickLocation, attribusionHash); - } -} diff --git a/LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs b/LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs deleted file mode 100644 index a51d31a..0000000 --- a/LiteCharms.Features/Shop/Leads/Commands/Handlers/CreateLeadCommandHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Utilities.Hash.Commands; - -namespace LiteCharms.Features.Leads.Commands.Handlers; - -public class CreateLeadCommandHandler(IDbContextFactory contextFactory, ISender mediator) : IRequestHandler> -{ - public async ValueTask> Handle(CreateLeadCommand request, CancellationToken cancellationToken) - { - try - { - var hashCommand = ComputeHashCommand.Create($"{request.ClickId}{request.AppClickId}{request.WebClickId}"); - var hashResult = await mediator.Send(hashCommand, cancellationToken); - - if(hashResult.IsFailed) - return Result.Fail(new Error($"Failed to compute hash for lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}") - .CausedBy(hashResult.Errors)); - - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var newLead = context.Leads.Add(new Entities.Lead - { - WebClickId = request.WebClickId, - AppClickId = request.AppClickId, - Source = request.Source, - ClickId = request.ClickId, - AdGroupId = request.AdGroupId, - AdName = request.AdName, - CampaignId = request.CampaignId, - ClickLocation = request.ClickLocation, - CustomerId = request.CustomerId, - FeedItemId = request.FeedItemId, - Status = Models.LeadStatus.New, - TargetId = request.TargetId, - AttributionHash = hashResult.Value - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(newLead.Entity.Id) - : Result.Fail(new Error($"Failed to create lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs b/LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs deleted file mode 100644 index 67f14fc..0000000 --- a/LiteCharms.Features/Shop/Leads/Commands/Handlers/UpdateLeadCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Leads.Commands.Handlers; - -public class UpdateLeadCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdateLeadCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var lead = await context.Leads.FirstOrDefaultAsync(l => l.Id == request.LeadId, cancellationToken); - - if (lead is null) - return Result.Fail(new Error($"Lead with ID {request.LeadId} not found.")); - - lead.Status = request.Status; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail(new Error($"Failed to update the lead {request.LeadId}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs b/LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs deleted file mode 100644 index ef31b01..0000000 --- a/LiteCharms.Features/Shop/Leads/Commands/UpdateLeadCommand.cs +++ /dev/null @@ -1,28 +0,0 @@ -using LiteCharms.Features.Shop; -using LiteCharms.Models; - -namespace LiteCharms.Features.Leads.Commands; - -public class UpdateLeadCommand : IRequest -{ - public Guid LeadId { get; set; } - - public LeadStatus Status { get; set; } - - private UpdateLeadCommand(Guid leadId, LeadStatus status) - { - LeadId = leadId; - Status = status; - } - - public static UpdateLeadCommand Create(Guid leadId, LeadStatus status) - { - if (leadId == Guid.Empty) - throw new ArgumentException("Lead ID cannot be empty.", nameof(leadId)); - - if (!Enum.IsDefined(typeof(LeadStatus), status)) - throw new ArgumentException("Invalid lead status.", nameof(status)); - - return new(leadId, status); - } -} diff --git a/LiteCharms.Features/Shop/Leads/LeadService.cs b/LiteCharms.Features/Shop/Leads/LeadService.cs new file mode 100644 index 0000000..f8b4637 --- /dev/null +++ b/LiteCharms.Features/Shop/Leads/LeadService.cs @@ -0,0 +1,116 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.Leads.Models; +using LiteCharms.Features.Shop.Postgres; +using static LiteCharms.Features.Extensions.Hash; + +namespace LiteCharms.Features.Shop.Leads; + +public class LeadService(IDbContextFactory contextFactory) +{ + public async ValueTask> CreateLeadAsync(CreateLead request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var newLead = context.Leads.Add(new Entities.Lead + { + WebClickId = request.WebClickId, + AppClickId = request.AppClickId, + Source = request.Source, + ClickId = request.ClickId, + AdGroupId = request.AdGroupId, + AdName = request.AdName, + CampaignId = request.CampaignId, + ClickLocation = request.ClickLocation, + CustomerId = request.CustomerId, + FeedItemId = request.FeedItemId, + Status = LeadStatus.New, + TargetId = request.TargetId, + AttributionHash = GenerateSha256HashString.Invoke($"{request.ClickId}{request.AppClickId}{request.WebClickId}") + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newLead.Entity.Id) + : Result.Fail(new Error($"Failed to create lead -> Google ClickId: {request.ClickId}, App ClickId: {request.AppClickId}, Web ClickId: {request.WebClickId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerLeadsAsync(Guid customerId, DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var leads = await context.Leads.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(lead => lead.CustomerId == customerId) + .Where(lead => lead.CreatedAt.Date >= fromDate && lead.CreatedAt.Date <= toDate) + .ToArrayAsync(cancellationToken); + + return leads?.Length > 0 + ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) + : Result.Fail(new Error($"No customer {customerId} leads found for the specified date range {range.From} to {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetLeadsAsync(DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var leads = await context.Leads.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(l => l.CreatedAt.Date >= fromDate && l.CreatedAt.Date <= toDate) + .Take(range.MaxRecords) + .ToArrayAsync(cancellationToken); + + return leads?.Length > 0 + ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) + : Result.Fail(new Error($"No leads found for the specified date range {range.From} to {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateLeadAsync(Guid leadId, LeadStatus status, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var lead = await context.Leads.FirstOrDefaultAsync(l => l.Id == leadId, cancellationToken); + + if (lead is null) + return Result.Fail(new Error($"Lead with ID {leadId} not found.")); + + lead.Status = status; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to update the lead {leadId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Shop/Leads/Models/Records.cs b/LiteCharms.Features/Shop/Leads/Models/Records.cs new file mode 100644 index 0000000..9037942 --- /dev/null +++ b/LiteCharms.Features/Shop/Leads/Models/Records.cs @@ -0,0 +1,28 @@ +namespace LiteCharms.Features.Shop.Leads.Models; + +public record CreateLead +{ + public Guid? CustomerId { get; set; } + + public required string Source { get; set; } + + public required string ClickId { get; set; } + + public required string WebClickId { get; set; } + + public required string AppClickId { get; set; } + + public long? CampaignId { get; set; } + + public long? AdGroupId { get; set; } + + public long? AdName { get; set; } + + public long? TargetId { get; set; } + + public long? FeedItemId { get; set; } + + public string? ClickLocation { get; set; } + + public string? AttribusionHash { get; set; } +} diff --git a/LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs b/LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs deleted file mode 100644 index de53634..0000000 --- a/LiteCharms.Features/Shop/Leads/Queries/GetCustomerLeadsQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Leads.Models; - -namespace LiteCharms.Features.Leads.Queries; - -public class GetCustomerLeadsQuery : IRequest> -{ - public Guid CustomerId { get; } - - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - private GetCustomerLeadsQuery(Guid customerId, DateOnly from, DateOnly to) - { - CustomerId = customerId; - From = from; - To = to; - } - - public static GetCustomerLeadsQuery Create(Guid customerId, DateOnly from, DateOnly to) - { - if(customerId == Guid.Empty) - throw new ArgumentException("Customer ID cannot be empty.", nameof(customerId)); - - if(from > to) - throw new ArgumentException("The 'From' date cannot be later than the 'To' date."); - - return new(customerId, from, to); - } -} diff --git a/LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs b/LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs deleted file mode 100644 index d280e65..0000000 --- a/LiteCharms.Features/Shop/Leads/Queries/GetLeadsQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Leads.Models; - -namespace LiteCharms.Features.Leads.Queries; - -public class GetLeadsQuery : IRequest> -{ - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - public int MaxRecords { get; set; } - - private GetLeadsQuery(DateOnly from, DateOnly to, int maxRecords = 1000) - { - From = from; - To = to; - MaxRecords = maxRecords; - } - - public static GetLeadsQuery 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/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs deleted file mode 100644 index dd9ba15..0000000 --- a/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetCustomerLeadsQueryHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Leads.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Leads.Queries.Handlers; - -public class GetCustomerLeadsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetCustomerLeadsQuery 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 leads = await context.Leads.AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(lead => lead.CustomerId == request.CustomerId) - .Where(lead => lead.CreatedAt.Date >= fromDate && lead.CreatedAt.Date <= toDate) - .ToArrayAsync(cancellationToken); - - return leads?.Length > 0 - ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) - : Result.Fail(new Error($"No customer {request.CustomerId} leads found for the specified date range {request.From} to {request.To}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs b/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs deleted file mode 100644 index 417ff5c..0000000 --- a/LiteCharms.Features/Shop/Leads/Queries/Handlers/GetLeadsQueryHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Leads.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Leads.Queries.Handlers; - -public class GetLeadsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetLeadsQuery 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 leads = await context.Leads.AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(l => l.CreatedAt.Date >= fromDate && l.CreatedAt.Date <= toDate) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return leads?.Length > 0 - ? Result.Ok(leads.Select(l => l.ToModel()).ToArray()) - : Result.Fail(new Error($"No leads found for the specified date range {request.From} to {request.To}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs b/LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs deleted file mode 100644 index 59e69d6..0000000 --- a/LiteCharms.Features/Shop/Notifications/Commands/CreateNotificationCommand.cs +++ /dev/null @@ -1,73 +0,0 @@ -using LiteCharms.Features.Shop; -using LiteCharms.Models; - -namespace LiteCharms.Features.Notifications.Commands; - -public class CreateNotification : IRequest> -{ - public NotificationDirection Direction { get; set; } - - public string? Sender { get; set; } - - public string? SenderAddress { get; set; } - - public string? Subject { get; set; } - - public string? Message { get; set; } - - public NotificationPlatforms Platform { get; set; } - - public Priorities Priority { get; set; } - - public string? Recipient { get; set; } - - public string? RecipientAddress { get; set; } - - public string? CorrelationId { get; set; } - - public CorrelationIdTypes CorrelationIdType { get; set; } - - public bool IsInternal { get; set; } - - public bool IsHtml { get; set; } - - private CreateNotification(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) - { - Direction = direction; - Sender = sender; - SenderAddress = senderAddress; - Subject = subject; - Message = message; - Platform = platform; - Priority = priority; - Recipient = recipient; - RecipientAddress = recipientAddress; - CorrelationId = correlationId; - CorrelationIdType = correlationIdType; - IsInternal = isInternal; - IsHtml = isHtml; - } - - public static CreateNotification Create(NotificationDirection direction, string sender, string senderAddress, string subject, string message, NotificationPlatforms platform, Priorities priority, string recipient, string recipientAddress, string correlationId, CorrelationIdTypes correlationIdType, bool isInternal, bool isHtml = false) - { - if (string.IsNullOrWhiteSpace(sender)) - throw new ArgumentException("Sender name is required.", nameof(sender)); - - if (string.IsNullOrWhiteSpace(subject)) - throw new ArgumentException("Subject is required.", nameof(subject)); - - if (string.IsNullOrWhiteSpace(message)) - throw new ArgumentException("Message is required.", nameof(message)); - - if (string.IsNullOrWhiteSpace(recipient)) - throw new ArgumentException("Recipient name is required.", nameof(recipient)); - - if (string.IsNullOrWhiteSpace(recipientAddress)) - throw new ArgumentException("Recipient address is required.", nameof(recipientAddress)); - - if (string.IsNullOrWhiteSpace(correlationId)) - throw new ArgumentException("CorrelationId is required.", nameof(correlationId)); - - return new(direction, sender, senderAddress, subject, message, platform, priority, recipient, recipientAddress, correlationId, correlationIdType, isInternal, isHtml); - } -} diff --git a/LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs deleted file mode 100644 index facd9a3..0000000 --- a/LiteCharms.Features/Shop/Notifications/Commands/Handlers/CreateNotificationCommandHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Notifications.Commands.Handlers; - -public class CreateNotificationCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(CreateNotification request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var newNotification = context.Notifications.Add(new Entities.Notification - { - Direction = request.Direction, - SenderName = request.Sender, - Sender = request.SenderAddress, - Recipient = request.Recipient, - RecipientAddress = request.RecipientAddress, - Subject = request.Subject, - Message = request.Message, - Platform = request.Platform, - Priority = request.Priority, - CorrelationId = request.CorrelationId, - CorrelationIdType = request.CorrelationIdType, - IsInternal = request.IsInternal, - IsHtml = request.IsHtml, - Processed = false - }); - - 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/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs b/LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs deleted file mode 100644 index 6d32acc..0000000 --- a/LiteCharms.Features/Shop/Notifications/Commands/Handlers/UpdateNotificationCommandHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -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; - - if (request.HasError) - { - notification.HasError = request.HasError; - notification.Errors = request.Errors; - } - - 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/Shop/Notifications/Commands/UpdateNotificationCommand.cs b/LiteCharms.Features/Shop/Notifications/Commands/UpdateNotificationCommand.cs deleted file mode 100644 index 950442d..0000000 --- a/LiteCharms.Features/Shop/Notifications/Commands/UpdateNotificationCommand.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace LiteCharms.Features.Notifications.Commands; - -public class UpdateNotificationCommand : IRequest -{ - public Guid NotificationId { get; set; } - - public bool Processed { get; set; } - - public bool HasError { get; set; } - - public string[]? Errors { get; set; } - - private UpdateNotificationCommand(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null) - { - NotificationId = notificationId; - Processed = processed; - HasError = hasError; - Errors = errors; - } - - public static UpdateNotificationCommand Create(Guid notificationId, bool processed, bool hasError = false, string[]? errors = null) - { - if(notificationId == Guid.Empty) - throw new ArgumentException("Notification ID cannot be empty.", nameof(notificationId)); - - return new(notificationId, processed); - } -} diff --git a/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs index 37b7659..8a00b68 100644 --- a/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs +++ b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs @@ -13,10 +13,10 @@ public class NotificationConfiguration : IEntityTypeConfiguration builder.Property(f => f.Platform).IsRequired().HasConversion(); builder.Property(f => f.Priority).IsRequired().HasConversion(); builder.Property(f => f.CorrelationIdType).IsRequired().HasConversion(); - builder.Property(f => f.Sender).IsRequired(); + builder.Property(f => f.SenderAddress).IsRequired(); builder.Property(f => f.Subject).IsRequired(); builder.Property(f => f.Message).IsRequired(); - builder.Property(f => f.Recipient).IsRequired(); + builder.Property(f => f.RecipientName).IsRequired(); builder.Property(f => f.RecipientAddress).IsRequired(); builder.Property(f => f.CorrelationId).IsRequired(); builder.Property(f => f.IsHtml).HasDefaultValue(false); diff --git a/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs b/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs index 332ec86..6a11fda 100644 --- a/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs +++ b/LiteCharms.Features/Shop/Notifications/Events/Handlers/ProcessEmailNotificationsEventHandler.cs @@ -1,13 +1,15 @@ -using LiteCharms.Features.Email.Commands; +using LiteCharms.Features.Email; using LiteCharms.Features.Shop.Notifications.Entities; using LiteCharms.Features.Shop.Postgres; -using static LiteCharms.Features.ServiceBus.Constants; +using static LiteCharms.Features.Extensions.Timezones; -namespace LiteCharms.Features.Notifications.Events.Handlers; +namespace LiteCharms.Features.Shop.Notifications.Events.Handlers; -public class ProcessEmailNotificationsEventHandler(IDbContextFactory contextFactory, ILogger logger, ISender mediator) : - INotificationHandler +public class ProcessEmailNotificationsEventHandler(IDbContextFactory contextFactory, ILogger logger, + EmailService emailService) : INotificationHandler { + private bool dropBatch = false; + public async ValueTask Handle(ProcessEmailNotificationsEvent message, CancellationToken cancellationToken) { try @@ -17,14 +19,16 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory o.Priority) .ThenBy(o => o.CreatedAt) - .Where(n => n.CorrelationIdType == Models.CorrelationIdTypes.Email) - .Where(n => n.Direction == Models.NotificationDirection.Outgoing) + .Where(n => n.CorrelationIdType == CorrelationIdTypes.Email) + .Where(n => n.Direction == NotificationDirection.Outgoing) .Take(message.MaxRecords) .ToListAsync(cancellationToken); foreach (var notification in notifications) { - var sendResult = await SendEmailAsync(notification, cancellationToken); + if (dropBatch || cancellationToken.IsCancellationRequested) break; + + var sendResult = await SendEmailAsync(notification,emailService, cancellationToken); if(sendResult.IsFailed) { @@ -40,6 +44,7 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory SendEmailAsync(Notification notification, CancellationToken cancellationToken = default) + private async Task SendEmailAsync(Notification notification, EmailService service, CancellationToken cancellationToken = default) { try { - var request = SendEmailCommand.Create(notification.Sender!, notification.SenderName!, ShopEmailFromAddress, - ShopEmailFromName, notification.Subject!, notification.Message!); + using Email.Models.Message message = CreateMessage(notification); - var result = await mediator.Send(request, cancellationToken); + var sendResult = await service.SendEmailAsync(message, cancellationToken); - return result.IsFailed - ? Result.Fail(result.Errors) + if (sendResult.IsFailed) + { + if (emailService.Status != EmailStatuses.Success && emailService.Status != EmailStatuses.Connected) dropBatch = true; + + return Result.Fail(sendResult.Errors); + } + + return sendResult.IsFailed + ? Result.Fail(sendResult.Errors) : Result.Ok(); } catch (Exception ex) @@ -68,4 +79,29 @@ public class ProcessEmailNotificationsEventHandler(IDbContextFactory + new() + { + Sender = new Email.Models.Party + { + Name = notification.SenderName, + Address = notification.SenderAddress + }, + Recipient = new Email.Models.Party + { + Name = notification.RecipientName, + Address = notification.RecipientAddress + }, + Subject = notification.Subject, + Body = new Email.Models.Body + { + Properties = new Email.Models.BodyProperties + { + HasAttachments = false, + IsHtml = notification.IsHtml + }, + Message = notification.Message + } + }; } diff --git a/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs b/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs index bbc4da4..d496ab9 100644 --- a/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs +++ b/LiteCharms.Features/Shop/Notifications/Events/ProcessEmailNotificationsEvent.cs @@ -1,6 +1,6 @@ using LiteCharms.Features.Abstractions; -namespace LiteCharms.Features.Notifications.Events; +namespace LiteCharms.Features.Shop.Notifications.Events; public class ProcessEmailNotificationsEvent : EventBase, IEvent { diff --git a/LiteCharms.Features/Shop/Notifications/INotificationService.cs b/LiteCharms.Features/Shop/Notifications/INotificationService.cs deleted file mode 100644 index d036de3..0000000 --- a/LiteCharms.Features/Shop/Notifications/INotificationService.cs +++ /dev/null @@ -1,9 +0,0 @@ -using LiteCharms.Features.Shop.Notifications.Models; - -namespace LiteCharms.Features.Shop.Notifications; - -public interface INotificationService -{ - Task> CreateNotificationAsync(CreateNotification request, CancellationToken cancellationToken = default); - Task UpdateNotificationAsync(UpdateNotification request, CancellationToken cancellationToken = default); -} diff --git a/LiteCharms.Features/Shop/Notifications/Models/Notification.cs b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs index cba815c..bb9b77c 100644 --- a/LiteCharms.Features/Shop/Notifications/Models/Notification.cs +++ b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs @@ -16,7 +16,7 @@ public class Notification public CorrelationIdTypes CorrelationIdType { get; set; } - public string? Sender { get; set; } + public string? SenderAddress { get; set; } public string? SenderName { get; set; } @@ -24,7 +24,7 @@ public class Notification public string? Message { get; set; } - public string? Recipient { get; set; } + public string? RecipientName { get; set; } public string? RecipientAddress { get; set; } diff --git a/LiteCharms.Features/Shop/Notifications/Models/Records.cs b/LiteCharms.Features/Shop/Notifications/Models/Records.cs index 2f0f949..6d41f26 100644 --- a/LiteCharms.Features/Shop/Notifications/Models/Records.cs +++ b/LiteCharms.Features/Shop/Notifications/Models/Records.cs @@ -2,23 +2,23 @@ public record CreateNotification { - public NotificationDirection Direction { get; set; } + public required NotificationDirection Direction { get; set; } - public string? Sender { get; set; } + public required string Sender { get; set; } - public string? SenderAddress { get; set; } + public required string SenderAddress { get; set; } - public string? Subject { get; set; } + public required string Subject { get; set; } public string? Message { get; set; } - public NotificationPlatforms Platform { get; set; } + public required NotificationPlatforms Platform { get; set; } - public Priorities Priority { get; set; } + public required Priorities Priority { get; set; } - public string? Recipient { get; set; } + public required string Recipient { get; set; } - public string? RecipientAddress { get; set; } + public required string RecipientAddress { get; set; } public string? CorrelationId { get; set; } @@ -31,9 +31,9 @@ public record CreateNotification public class UpdateNotification { - public Guid NotificationId { get; set; } + public required Guid NotificationId { get; set; } - public bool Processed { get; set; } + public required bool Processed { get; set; } public bool HasError { get; set; } diff --git a/LiteCharms.Features/Shop/Notifications/NotificationService.cs b/LiteCharms.Features/Shop/Notifications/NotificationService.cs index 02ee707..84e7565 100644 --- a/LiteCharms.Features/Shop/Notifications/NotificationService.cs +++ b/LiteCharms.Features/Shop/Notifications/NotificationService.cs @@ -1,5 +1,115 @@ -namespace LiteCharms.Features.Shop.Notifications; +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.Notifications.Models; +using LiteCharms.Features.Shop.Postgres; -public class NotificationService : INotificationService +namespace LiteCharms.Features.Shop.Notifications; + +public class NotificationService(IDbContextFactory contextFactory) { + public async ValueTask> CreateNotificationAsync(CreateNotification request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var newNotification = context.Notifications.Add(new Entities.Notification + { + Direction = request.Direction, + SenderName = request.Sender, + SenderAddress = request.SenderAddress, + RecipientName = request.Recipient, + RecipientAddress = request.RecipientAddress, + Subject = request.Subject, + Message = request.Message, + Platform = request.Platform, + Priority = request.Priority, + CorrelationId = request.CorrelationId, + CorrelationIdType = request.CorrelationIdType, + IsInternal = request.IsInternal, + IsHtml = request.IsHtml, + Processed = false + }); + + 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)); + } + } + + public async ValueTask> GetNotificationAsync(Guid notificationId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var notification = await context.Notifications.FirstOrDefaultAsync(n => n.Id == notificationId, cancellationToken); + + return notification is not null + ? Result.Ok(notification.ToModel()) + : Result.Fail(new Error($"Notification with id {notificationId} not found")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetNotificationsAsync(DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var notifications = await context.Notifications.AsNoTracking() + .Where(n => n.CreatedAt >= fromDate && n.CreatedAt <= toDate) + .OrderByDescending(n => n.CreatedAt) + .Take(range.MaxRecords) + .ToArrayAsync(cancellationToken); + + return notifications?.Length > 0 + ? Result.Ok(notifications.Select(n => n.ToModel()).ToArray()) + : Result.Fail(new Error($"No notifications found for the specified date range {range.From} to {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateNotificationAsync(UpdateNotification request, CancellationToken cancellationToken = default) + { + 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; + + if (request.HasError) + { + notification.HasError = request.HasError; + notification.Errors = request.Errors; + } + + 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/Shop/Notifications/Queries/GetNotificationQuery.cs b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs deleted file mode 100644 index 6c599fb..0000000 --- a/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Notifications.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/Shop/Notifications/Queries/GetNotificationsQuery.cs b/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs deleted file mode 100644 index 6a10a4e..0000000 --- a/LiteCharms.Features/Shop/Notifications/Queries/GetNotificationsQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Notifications.Models; - -namespace LiteCharms.Features.Notifications.Queries; - -public class GetNotificationsQuery : IRequest> -{ - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - public int MaxRecords { get; set; } - - private GetNotificationsQuery(DateOnly from, DateOnly to, int maxRecords = 1000) - { - From = from; - To = to; - MaxRecords = maxRecords; - } - - public static GetNotificationsQuery 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.", nameof(maxRecords)); - - return new(from, to, maxRecords); - } -} diff --git a/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs deleted file mode 100644 index 11d5553..0000000 --- a/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationQueryHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Notifications.Models; -using LiteCharms.Features.Shop.Postgres; - -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.FirstOrDefaultAsync(n => n.Id == 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/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs b/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs deleted file mode 100644 index 17e6094..0000000 --- a/LiteCharms.Features/Shop/Notifications/Queries/Handlers/GetNotificationsQueryHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Notifications.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Notifications.Queries.Handlers; - -public class GetNotificationsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetNotificationsQuery 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 notifications = await context.Notifications.AsNoTracking() - .Where(n => n.CreatedAt >= fromDate && n.CreatedAt <= toDate) - .OrderByDescending(n => n.CreatedAt) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return notifications?.Length > 0 - ? Result.Ok(notifications.Select(n => n.ToModel()).ToArray()) - : Result.Fail(new Error($"No notifications found for the specified date range {request.From} to {request.To}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs b/LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs deleted file mode 100644 index 8baf7e1..0000000 --- a/LiteCharms.Features/Shop/Orders/Commands/CreateOrderCommand.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace LiteCharms.Features.Orders.Commands; - -public class CreateOrderCommand : IRequest> -{ - public Guid CustomerId { get; set; } - - public Guid ShoppingCartId { get; set; } - - public Guid? QuoteId { get; set; } - - public string[]? Requirements { get; set; } - - public string[]? Notes { get; set; } - - public string[]? Terms { get; set; } - - public bool DepositRequired { get; set; } - - private CreateOrderCommand(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null) - { - CustomerId = customerId; - ShoppingCartId = shoppingCartId; - DepositRequired = depositRequired; - QuoteId = quoteId; - Requirements = requirements; - Notes = notes; - Terms = terms; - } - - public static CreateOrderCommand Create(Guid customerId, Guid shoppingCartId, bool depositRequired, Guid? quoteId = null, string[]? requirements = null, string[]? notes = null, string[]? terms = null) - { - if (customerId == Guid.Empty) - throw new ArgumentException("CustomerId is required.", nameof(customerId)); - - if (shoppingCartId == Guid.Empty) - throw new ArgumentException("ShoppingCartId is required.", nameof(shoppingCartId)); - - return new(customerId, shoppingCartId, depositRequired, quoteId, requirements, notes, terms); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs deleted file mode 100644 index 95a599a..0000000 --- a/LiteCharms.Features/Shop/Orders/Commands/Handlers/CreateOrderCommandHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using LiteCharms.Features.Shop; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Models; - -namespace LiteCharms.Features.Orders.Commands.Handlers; - -public class CreateOrderCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(CreateOrderCommand 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 {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 - { - CreatedAt = DateTime.UtcNow, - Status = OrderStatus.Pending, - CustomerId = request.CustomerId, - QuoteId = request.QuoteId, - ShoppingCartId = request.ShoppingCartId, - DepositRequired = request.DepositRequired, - Requirements = request.Requirements, - Notes = request.Notes, - Terms = request.Terms - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(newOrder.Entity.Id) - : Result.Fail(new Error($"Failed to create customer {request.CustomerId} order using shopping cart {request.ShoppingCartId}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs deleted file mode 100644 index 2c0052f..0000000 --- a/LiteCharms.Features/Shop/Orders/Commands/Handlers/UpdateOrderStatusCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Orders.Commands.Handlers; - -public class UpdateOrderStatusCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdateOrderStatusCommand 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 {request.OrderId} not found")); - - order.Status = request.Status; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail(new Error($"Failed to update order {request.OrderId}")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs b/LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs deleted file mode 100644 index eefc734..0000000 --- a/LiteCharms.Features/Shop/Orders/Commands/UpdateOrderStatusCommand.cs +++ /dev/null @@ -1,31 +0,0 @@ -using LiteCharms.Features.Shop; -using LiteCharms.Models; - -namespace LiteCharms.Features.Orders.Commands; - -public class UpdateOrderStatusCommand : IRequest -{ - public Guid OrderId { get; set; } - - public OrderStatus Status { get; set; } - - public string? Note { get; set; } - - private UpdateOrderStatusCommand(Guid orderId, OrderStatus status, string? note) - { - OrderId = orderId; - Status = status; - Note = note; - } - - public static UpdateOrderStatusCommand Create(Guid orderId, OrderStatus status, string? note) - { - if (orderId == Guid.Empty) - throw new ArgumentException("OrderId is required.", nameof(orderId)); - - if (!Enum.IsDefined(typeof(OrderStatus), status)) - throw new ArgumentException("Invalid order status.", nameof(status)); - - return new(orderId, status, note); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Models/Records.cs b/LiteCharms.Features/Shop/Orders/Models/Records.cs new file mode 100644 index 0000000..21dde34 --- /dev/null +++ b/LiteCharms.Features/Shop/Orders/Models/Records.cs @@ -0,0 +1,40 @@ +namespace LiteCharms.Features.Shop.Orders.Models; + +public record CreateOrder +{ + public required Guid CustomerId { get; set; } + + public required Guid ShoppingCartId { get; set; } + + public Guid? QuoteId { get; set; } + + public string[]? Requirements { get; set; } + + public string[]? Notes { get; set; } + + public string[]? Terms { get; set; } +} + +public record UpdateOrder +{ + public required Guid OrderId { get; set; } + + public required OrderStatus Status { get; set; } + + public string? InvoiceUrl { get; set; } + + public string[]? Notes { get; set; } + + public string[]? Requirements { get; set; } +} + +public record RefundCustomer +{ + public required Guid OrderId { get; set; } + + public required Guid CustomerId { get; set; } + + public required string Reason { get; set; } + + public required decimal Amount { get; set; } +} \ No newline at end of file diff --git a/LiteCharms.Features/Shop/Orders/OrderService.cs b/LiteCharms.Features/Shop/Orders/OrderService.cs new file mode 100644 index 0000000..705a099 --- /dev/null +++ b/LiteCharms.Features/Shop/Orders/OrderService.cs @@ -0,0 +1,260 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.Orders.Models; +using LiteCharms.Features.Shop.Postgres; +using static LiteCharms.Features.Extensions.Timezones; + +namespace LiteCharms.Features.Shop.Orders; + +public class OrderService(IDbContextFactory contextFactory) +{ + public async ValueTask> CreateOrderAsync(CreateOrder request, CancellationToken cancellationToken = default) + { + 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 {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 + { + Status = OrderStatus.Pending, + CustomerId = request.CustomerId, + Requirements = request.Requirements, + Notes = request.Notes, + Terms = request.Terms + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newOrder.Entity.Id) + : Result.Fail(new Error($"Failed to create customer {request.CustomerId} order using shopping cart {request.ShoppingCartId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerOrderRefundsAsync(Guid customerId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id {customerId} does not exist.")); + + var refunds = await context.OrderRefunds.AsNoTracking().AsSplitQuery() + .OrderByDescending(o => o.CreatedAt) + .Where(r => r.Order!.CustomerId == customerId).ToArrayAsync(cancellationToken); + + return refunds?.Length > 0 + ? Result.Ok(refunds.Select(r => r.ToModel()).ToArray()) + : Result.Fail(new Error($"No refunds found for customer with Id {customerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerOrdersAsync(Guid customerId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AsNoTracking().AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id {customerId} does not exist.")); + + var orders = await context.Orders.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(o => o.CustomerId == customerId) + .ToArrayAsync(cancellationToken); + + return orders?.Length > 0 + ? Result.Ok(orders.Select(o => o.ToModel()).ToArray()) + : Result.Fail(new Error($"No orders found for customer with Id {customerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrderRefundAsync(Guid orderId, Guid orderRefundId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refund = await context.OrderRefunds.AsNoTracking() + .FirstOrDefaultAsync(r => r.OrderId == orderId && r.Id == orderRefundId, cancellationToken); + + return refund is not null + ? Result.Ok(refund.ToModel()) + : Result.Fail(new Error($"Refund {orderRefundId} not found for the given OrderId: {orderId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrderRefundAsync(Guid orderRefundId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refund = await context.OrderRefunds.AsNoTracking().FirstOrDefaultAsync(r => r.Id == orderRefundId, cancellationToken); + + return refund is not null + ? Result.Ok(refund.ToModel()) + : Result.Fail($"Order refund could not be found with id {orderRefundId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrderRefundsAsync(Guid orderId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refunds = await context.OrderRefunds.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(r => r.OrderId == orderId) + .ToArrayAsync(cancellationToken); + + return refunds?.Length > 0 + ? Result.Ok(refunds.Select(r => r.ToModel()).ToArray()) + : Result.Fail($"Order refunds could not be found with order id {orderId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetOrdersAsync(DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.To.ToDateTime(TimeOnly.MaxValue); + + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var orders = await context.Orders + .AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate) + .Take(range.MaxRecords) + .ToArrayAsync(cancellationToken); + + return orders?.Length > 0 + ? Result.Ok(orders.Select(o => o.ToModel()).ToArray()) + : Result.Fail(new Error($"No orders found for the specified date range {range.From} - {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> RefundCustomerAsync(RefundCustomer request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Orders.AnyAsync(o => o.Id == request.OrderId, cancellationToken)) + return Result.Fail(new Error($"Order with Id: {request.OrderId} does not exist")); + + if (!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id: {request.CustomerId} does not exist")); + + if (!await context.Orders.AnyAsync(o => o.Id == request.OrderId && o.CustomerId == request.CustomerId, cancellationToken)) + return Result.Fail(new Error($"Order with Id: {request.OrderId} does not belong to Customer with Id: {request.CustomerId}")); + + var refund = context.OrderRefunds.Add(new Entities.OrderRefund + { + OrderId = request.OrderId, + Reason = request.Reason, + Amount = request.Amount + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(refund.Entity.Id) + : Result.Fail(new Error($"Failed to create refund for OrderId: {request.OrderId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateOrderRefundAsync(Guid orderRefundId, string reason, decimal amount, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var refund = await context.OrderRefunds.FirstOrDefaultAsync(r => r.Id == orderRefundId, cancellationToken); + + if (refund is null) + return Result.Fail($"Order refund not found with id {orderRefundId}"); + + refund.Reason = reason; + refund.Amount = amount; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to update order refund {orderRefundId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateOrderStatusAsync(UpdateOrder request, CancellationToken cancellationToken = default) + { + 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 {request.OrderId} not found")); + + order.Status = request.Status; + order.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + if(!string.IsNullOrWhiteSpace(request.InvoiceUrl)) order.InvoiceUrl = request.InvoiceUrl; + + if(request.Requirements?.Length > 0) order.Requirements = request.Requirements; + if(request.Notes?.Length > 0) order.Notes = request.Notes; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to update order {request.OrderId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs deleted file mode 100644 index f74f5c4..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/GetCustomerOrdersQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Models; - -namespace LiteCharms.Features.Orders.Queries; - -public class GetCustomerOrdersQuery : IRequest> -{ - public Guid CustomerId { get; } - - private GetCustomerOrdersQuery(Guid customerId) => CustomerId = customerId; - - public static GetCustomerOrdersQuery Create(Guid customerId) - { - if (customerId == Guid.Empty) - throw new ArgumentException("CustomerId is required.", nameof(customerId)); - - return new(customerId); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs deleted file mode 100644 index 01295cf..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/GetOrderRefundQuery.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Models; - -namespace LiteCharms.Features.Orders.Queries; - -public class GetOrderRefundQuery : IRequest> -{ - public Guid OrderId { get; set; } - - public Guid OrderRefundId { get; set; } - - private GetOrderRefundQuery(Guid orderId, Guid orderRefundId) - { - OrderId = orderId; - OrderRefundId = orderRefundId; - } - - public static GetOrderRefundQuery Create(Guid orderId, Guid orderRefundId) - { - if (orderId == Guid.Empty) - throw new ArgumentException("OrderId is required.", nameof(orderId)); - - if (orderRefundId == Guid.Empty) - throw new ArgumentException("OrderRefundId is required.", nameof(orderRefundId)); - - return new(orderId, orderRefundId); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs b/LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs deleted file mode 100644 index 0fc45d8..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/GetOrdersQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Models; - -namespace LiteCharms.Features.Orders.Queries; - -public class GetOrdersQuery : IRequest> -{ - public DateOnly From { get; set; } - - public DateOnly To { get; set; } - - public int MaxRecords { get; set; } - - private GetOrdersQuery(DateOnly from, DateOnly to, int maxRecords = 1000) - { - From = from; - To = to; - MaxRecords = maxRecords; - } - - public static GetOrdersQuery 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); - } -} \ No newline at end of file diff --git a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs deleted file mode 100644 index a2f65d9..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetCustomerOrdersQueryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Orders.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Orders.Queries.Handlers; - -public class GetCustomerOrdersQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetCustomerOrdersQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if(!await context.Customers.AsNoTracking().AnyAsync(c => 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) - .ToArrayAsync(cancellationToken); - - return orders?.Length > 0 - ? Result.Ok(orders.Select(o => o.ToModel()).ToArray()) - : Result.Fail(new Error($"No orders found for customer with Id {request.CustomerId}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs deleted file mode 100644 index 3493b11..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrderRefundQueryHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Orders.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Orders.Queries.Handlers; - -public class GetOrderRefundQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetOrderRefundQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var refund = await context.OrderRefunds.AsNoTracking() - .FirstOrDefaultAsync(r => r.OrderId == request.OrderId && r.Id == request.OrderRefundId, cancellationToken); - - return refund is not null - ? Result.Ok(refund.ToModel()) - : Result.Fail(new Error($"Refund {request.OrderRefundId} not found for the given OrderId: {request.OrderId}")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs deleted file mode 100644 index 898f3fc..0000000 --- a/LiteCharms.Features/Shop/Orders/Queries/Handlers/GetOrdersQueryHandler.cs +++ /dev/null @@ -1,34 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Orders.Models; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Orders.Queries.Handlers; - -public class GetOrdersQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetOrdersQuery 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 orders = await context.Orders - .AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(o => o.CreatedAt >= fromDate && o.CreatedAt <= toDate) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return orders?.Length > 0 - ? Result.Ok(orders.Select(o => o.ToModel()).ToArray()) - : Result.Fail(new Error($"No orders 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/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs deleted file mode 100644 index 9013f10..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/RefundCustomerCommandHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Refunds.Commands; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Shop.Orders.Refunds.Commands.Handlers; - -public class RefundCustomerCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(RefundCustomerCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if(!await context.Orders.AnyAsync(o => o.Id == request.OrderId, cancellationToken)) - return Result.Fail(new Error($"Order with Id: {request.OrderId} does not exist")); - - if (!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken)) - return Result.Fail(new Error($"Customer with Id: {request.CustomerId} does not exist")); - - if(!await context.Orders.AnyAsync(o => o.Id == request.OrderId && o.CustomerId == request.CustomerId, cancellationToken)) - return Result.Fail(new Error($"Order with Id: {request.OrderId} does not belong to Customer with Id: {request.CustomerId}")); - - var refund = context.OrderRefunds.Add(new Entities.OrderRefund - { - OrderId = request.OrderId, - Reason = request.Reason, - Amount = request.Amount - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(refund.Entity.Id) - : Result.Fail(new Error($"Failed to create refund for OrderId: {request.OrderId}")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs deleted file mode 100644 index ac88a49..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Commands/Handlers/UpdateOrderRefundCommandHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Refunds.Commands; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Shop.Orders.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/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs deleted file mode 100644 index ac5c75b..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Commands/RefundCustomerCommand.cs +++ /dev/null @@ -1,38 +0,0 @@ -namespace LiteCharms.Features.Shop.Orders.Refunds.Commands; - -public class RefundCustomerCommand : IRequest> -{ - public Guid OrderId { get; set; } - - public Guid CustomerId { get; set; } - - public string? Reason { get; set; } - - public decimal Amount { get; set; } - - private RefundCustomerCommand(Guid orderId, Guid customerId, string? reason, decimal amount) - { - OrderId = orderId; - CustomerId = customerId; - Reason = reason; - Amount = amount; - CustomerId = customerId; - } - - public static RefundCustomerCommand Create(Guid orderId, Guid customerId, string? reason, decimal amount) - { - if (orderId == Guid.Empty) - throw new ArgumentException("OrderId is required", nameof(orderId)); - - if (customerId == Guid.Empty) - throw new ArgumentException("CustomerId is required", nameof(customerId)); - - if (amount <= 0) - throw new ArgumentException("Amount must be greater than zero", nameof(amount)); - - if (string.IsNullOrWhiteSpace(reason)) - throw new ArgumentException("Reason is required", nameof(reason)); - - return new(orderId, customerId, reason, amount); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs b/LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs deleted file mode 100644 index 9f69b2f..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Commands/UpdateOrderRefundCommand.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace LiteCharms.Features.Shop.Orders.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/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs deleted file mode 100644 index 56d19f6..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetCustomerRefundsQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Models; - -namespace LiteCharms.Features.Shop.Orders.Refunds.Queries; - -public class GetCustomerRefundsQuery : IRequest> -{ - public Guid CustomerId { get; set; } - - private GetCustomerRefundsQuery(Guid customerId) => CustomerId = customerId; - - public static GetCustomerRefundsQuery Create(Guid customerId) - { - if (customerId == Guid.Empty) - throw new ArgumentException("CustomerId is required.", nameof(customerId)); - - return new(customerId); - } -} diff --git a/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs deleted file mode 100644 index ddeaa7b..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Queries/GetRefundQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Orders.Models; - -namespace LiteCharms.Features.Shop.Orders.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/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs deleted file mode 100644 index 8439f88..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetCustomerRefundsQueryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Orders.Models; -using LiteCharms.Features.Shop.Orders.Refunds.Queries; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Shop.Orders.Refunds.Queries.Handlers; - -public class GetCustomerRefundsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetCustomerRefundsQuery 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 refunds = await context.OrderRefunds.AsNoTracking().AsSplitQuery() - .OrderByDescending(o => o.CreatedAt) - .Where(r => r.Order!.CustomerId == request.CustomerId).ToArrayAsync(cancellationToken); - - return refunds?.Length > 0 - ? Result.Ok(refunds.Select(r => r.ToModel()).ToArray()) - : Result.Fail(new Error($"No refunds found for customer with Id {request.CustomerId}.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs b/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs deleted file mode 100644 index a363945..0000000 --- a/LiteCharms.Features/Shop/Orders/Refunds/Queries/Handlers/GetRefundQueryHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Orders.Models; -using LiteCharms.Features.Shop.Orders.Refunds.Queries; -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.Shop.Orders.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/Shop/Products/Models/Records.cs b/LiteCharms.Features/Shop/Products/Models/Records.cs new file mode 100644 index 0000000..b5579b8 --- /dev/null +++ b/LiteCharms.Features/Shop/Products/Models/Records.cs @@ -0,0 +1,14 @@ +namespace LiteCharms.Features.Shop.Products.Models; + +public record CreateProduct +{ + public required string Name { get; set; } + + public required string Summary { get; set; } + + public required string Description { get; set; } + + public required string ImageUrl { get; set; } + + public string[]? Thumbnails { get; set; } +} diff --git a/LiteCharms.Features/Shop/Products/ProductService.cs b/LiteCharms.Features/Shop/Products/ProductService.cs new file mode 100644 index 0000000..4b9b294 --- /dev/null +++ b/LiteCharms.Features/Shop/Products/ProductService.cs @@ -0,0 +1,229 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Products.Models; +using static LiteCharms.Features.Extensions.Timezones; + +namespace LiteCharms.Features.Shop.Products; + +public class ProductService(IDbContextFactory contextFactory) +{ + public async ValueTask ChangeProductPriceStatusAsync(Guid productPriceId, bool active, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var price = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken); + + if (price is null) + return Result.Fail($"Could not find product price with ID {productPriceId}"); + + price.Active = active; + price.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to change product price by ID {productPriceId}"); + + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask ChangeProductStatusAsync(Guid productId, bool active, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var product = await context.Products.FirstOrDefaultAsync(p => p.Id == productId, cancellationToken); + + if (product is null) + return Result.Fail($"Could not find product with ID {productId}"); + + product.Active = active; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to change product status by ID {productId}"); + + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreateProductAsync(CreateProduct request, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Products.AnyAsync(p => p.Name == request.Name, cancellationToken)) + return Result.Fail($"A product by the same name '{request.Name}' already exists"); + + var newProduct = context.Products.Add(new Entities.Product + { + Name = request.Name, + Summary = request.Summary, + Description = request.Description, + ImageUrl = request.ImageUrl, + Thumbnails = request.Thumbnails + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newProduct.Entity.Id) + : Result.Fail($"Failed to create new product '{request.Name}'"); + + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreateProductPriceAsync(Guid productId, decimal price, decimal discount = 0, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var newProductPrice = context.ProductPrices.Add(new Entities.ProductPrice + { + Price = price, + Discount = discount, + ProductId = productId + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(newProductPrice.Entity.Id) + : Result.Fail($"Failed to create new product price for product id {productId}"); + + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetProductAsync(Guid productId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var product = await context.Products.FirstOrDefaultAsync(p => p.Id == productId, cancellationToken); + + return product is not null + ? Result.Ok(product.ToModel()) + : Result.Fail(new Error($"Product with ID {productId} not found.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetProductPriceAsync(Guid productPriceId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Products.AnyAsync(p => p.Id == productPriceId, cancellationToken)) + return Result.Fail(new Error($"Product {productPriceId} not found.")); + + var productPrice = await context.ProductPrices.AsNoTracking() + .OrderByDescending(pp => pp.CreatedAt) + .FirstOrDefaultAsync(pp => pp.Id == productPriceId, cancellationToken); + + return productPrice is not null + ? Result.Ok(productPrice.ToModel()) + : Result.Fail(new Error($"Product price {productPriceId} not found.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetProductPricesAsync(int maxRecords = 1000, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var products = await context.ProductPrices.AsNoTracking() + .OrderByDescending(o => o.Id) + .Take(maxRecords) + .ToArrayAsync(cancellationToken); + + return Result.Ok(products.Select(p => p.ToModel()).ToArray()); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetProductsAsync(int maxRecords = 1000, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var products = await context.Products.AsNoTracking() + .OrderByDescending(o => o.Id) + .Take(maxRecords) + .ToArrayAsync(cancellationToken); + + return Result.Ok(products.Select(p => p.ToModel()).ToArray()); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> ReplaceProductPriceAsync(Guid productPriceId, decimal price, decimal discount = 0, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var existingPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken); + + if (existingPrice is null) + return Result.Fail($"Could not find product price with ID {productPriceId}"); + + existingPrice.Active = false; + existingPrice.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + if (!(await context.SaveChangesAsync(cancellationToken) > 0)) + return Result.Fail($"Failed to deactivate existing price of ID {productPriceId}, try again later"); + + var result = await CreateProductPriceAsync(existingPrice.ProductId, price, discount, cancellationToken); + + if(result.IsFailed) + { + var deactivatedPrice = await context.ProductPrices.FirstOrDefaultAsync(p => p.Id == productPriceId, cancellationToken); + + existingPrice.Active = true; + existingPrice.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Fail("Reverted to old price, creation of new price failed") + : Result.Fail($"Failed to reactivate price of ID {productPriceId} after new price creation failed"); + } + + return Result.Ok(result.Value); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} diff --git a/LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs deleted file mode 100644 index f118bcc..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/GetProductPriceQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries; - -public class GetProductPriceQuery : IRequest> -{ - public Guid ProductId { get; set; } - - private GetProductPriceQuery(Guid productId) => ProductId = productId; - - public static GetProductPriceQuery Create(Guid productId) - { - if (productId == Guid.Empty) - throw new ArgumentException("ProductId is required.", nameof(productId)); - - return new(productId); - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs deleted file mode 100644 index 5ba4be2..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/GetProductPricesQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries; - -public class GetProductPricesQuery : IRequest> -{ - public int MaxRecords { get; set; } - - private GetProductPricesQuery(int maxRecords = 1000) => MaxRecords = maxRecords; - - public static GetProductPricesQuery Create(int maxRecords = 1000) - { - if (maxRecords <= 0) - throw new ArgumentOutOfRangeException(nameof(maxRecords), "MaxRecords must be greater than zero."); - - return new(maxRecords); - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs deleted file mode 100644 index cd811ac..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/GetProductQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries; - -public class GetProductQuery : IRequest> -{ - public Guid ProductId { get; set; } - - private GetProductQuery(Guid productId) => ProductId = productId; - - public static GetProductQuery Create(Guid productId) - { - if(productId == Guid.Empty) - throw new ArgumentException("Product ID is required.", nameof(productId)); - - return new(productId); - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs b/LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs deleted file mode 100644 index a60670d..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/GetProductsQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries; - -public class GetProductsQuery : IRequest> -{ - public int MaxRecords { get; set; } - - private GetProductsQuery(int maxRecords = 1000) => MaxRecords = maxRecords; - - public static GetProductsQuery Create(int maxRecords = 1000) - { - if (maxRecords <= 0) - throw new ArgumentException("MaxRecords must be a positive integer."); - - return new(maxRecords); - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs deleted file mode 100644 index c1383de..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPriceQueryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries.Handlers; - -public class GetProductPriceQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetProductPriceQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if(!await context.Products.AnyAsync(p => 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) - .FirstOrDefaultAsync(cancellationToken); - - return productPrice is not null - ? Result.Ok(productPrice.ToModel()) - : Result.Fail(new Error($"Product price {request.ProductId} not found.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs deleted file mode 100644 index 1bc5489..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductPricesQueryHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries.Handlers; - -public class GetProductPricesQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetProductPricesQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var products = await context.ProductPrices.AsNoTracking() - .OrderByDescending(o => o.Id) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return Result.Ok(products.Select(p => p.ToModel()).ToArray()); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs deleted file mode 100644 index d60591e..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductQueryHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries.Handlers; - -public class GetProductQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetProductQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var product = await context.Products.FirstOrDefaultAsync(p => p.Id == request.ProductId, cancellationToken); - - return product is not null - ? Result.Ok(product.ToModel()) - : Result.Fail(new Error($"Product with ID {request.ProductId} not found.")); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs b/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs deleted file mode 100644 index 0ad9f40..0000000 --- a/LiteCharms.Features/Shop/Products/Queries/Handlers/GetProductsQueryHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Products.Models; - -namespace LiteCharms.Features.Products.Queries.Handlers; - -public class GetProductsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetProductsQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - var products = await context.Products.AsNoTracking() - .OrderByDescending(o => o.Id) - .Take(request.MaxRecords) - .ToArrayAsync(cancellationToken); - - return Result.Ok(products.Select(p => p.ToModel()).ToArray()); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs deleted file mode 100644 index 498aa4a..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToOrderCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -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/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs deleted file mode 100644 index 61c0902..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/AssignQuoteToShoppingCartCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -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/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs deleted file mode 100644 index 266e8d9..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToOrderCommandHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -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/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs deleted file mode 100644 index a9f6b12..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/Handlers/AssignQuoteToShoppingCartCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -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/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs b/LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs deleted file mode 100644 index 8e80a52..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/Handlers/UpdateQuoteStatusCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -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/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs b/LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs deleted file mode 100644 index 14c9684..0000000 --- a/LiteCharms.Features/Shop/Quotes/Commands/UpdateQuoteStatusCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using LiteCharms.Features.Shop; -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/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs deleted file mode 100644 index 37adc17..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/GetCustomerQuotesQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/Queries/GetQuoteQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs deleted file mode 100644 index 756788e..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/GetQuoteQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/Queries/GetQuotesQuery.cs b/LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs deleted file mode 100644 index 4e7db83..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/GetQuotesQuery.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs deleted file mode 100644 index 811df36..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetCustomerQuotesQueryHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Quotes.Queries; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs deleted file mode 100644 index 35c2d39..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuoteQueryHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs b/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs deleted file mode 100644 index f7ad77b..0000000 --- a/LiteCharms.Features/Shop/Quotes/Queries/Handlers/GetQuotesHandler.cs +++ /dev/null @@ -1,33 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.Quotes.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/Shop/Quotes/QuoteService.cs b/LiteCharms.Features/Shop/Quotes/QuoteService.cs new file mode 100644 index 0000000..7e726c1 --- /dev/null +++ b/LiteCharms.Features/Shop/Quotes/QuoteService.cs @@ -0,0 +1,154 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Models; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.Quotes.Models; +using static LiteCharms.Features.Extensions.Timezones; + +namespace LiteCharms.Features.Shop.Quotes; + +public class QuoteService(IDbContextFactory contextFactory) +{ + public async ValueTask AssignQuoteToOrderAsync(Guid quoteId, Guid orderId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken); + + if (quote is null) + return Result.Fail(new Error($"Quote with id {orderId} not found")); + + if (!await context.Orders.AnyAsync(q => q.Id == orderId, cancellationToken)) + return Result.Fail(new Error($"Order with id {quoteId} not found")); + + if (quote.OrderId == orderId) + return Result.Fail(new Error($"Quote with id {quoteId} is already assigned to order with id {orderId}")); + + quote.OrderId = orderId; + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail(new Error($"Failed to assign quote with id {quoteId} to order with id {orderId}")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask AssignQuoteToShoppingCartAsync(Guid quoteId, Guid shoppingCartId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.FirstOrDefaultAsync(o => o.Id == quoteId, cancellationToken); + + if (quote is null) + return Result.Fail(new Error($"Quote with id {quoteId} not found")); + + if (!await context.ShoppingCarts.AnyAsync(q => q.Id == shoppingCartId, cancellationToken)) + return Result.Fail(new Error($"Shopping Cart with id {shoppingCartId} not found")); + + quote.ShoppingCartId = shoppingCartId; + + 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)); + } + } + + public async ValueTask> GetCustomerQuotesAsync(Guid customerId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id {customerId} does not exist.")); + + var quotes = await context.Quotes.AsNoTracking() + .Where(q => q.CustomerId == 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 {customerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetQuoteAsync(Guid quoteId, CancellationToken cancellationToken = default) + { + try + { + await using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.AsNoTracking().FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken); + + return quote is not null + ? Result.Ok(quote.ToModel()) + : Result.Fail(new Error($"Quote with ID {quoteId} not found.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetQuotesAsync(DateRange range, CancellationToken cancellationToken = default) + { + try + { + var fromDate = range.From.ToDateTime(TimeOnly.MinValue); + var toDate = range.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(range.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 {range.From} - {range.To}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateQuoteStatusAsync(Guid quoteId, QuoteStatus status, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var quote = await context.Quotes.FirstOrDefaultAsync(q => q.Id == quoteId, cancellationToken); + + if (quote is null) + return Result.Fail(new Error("Quote not found.")); + + quote.Status = status; + quote.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + 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/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs deleted file mode 100644 index 009fccd..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddItemToShoppingCartCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class AddItemToShoppingCartCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - public Guid ProductPriceId { get; set; } - - public int Quantity { get; set; } - - private AddItemToShoppingCartCommand(Guid shoppingCartId, Guid productPriceId, int quantity = 1) - { - ShoppingCartId = shoppingCartId; - ProductPriceId = productPriceId; - Quantity = quantity; - } - - public static AddItemToShoppingCartCommand Create(Guid shoppingCartId, Guid productPriceId, int quantity = 1) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId)); - - if (productPriceId == Guid.Empty) - throw new ArgumentException($"Product item required", nameof(productPriceId)); - - if(quantity <= 0) throw new ArgumentException($"Quantity must be at least 1", nameof(quantity)); - - return new(shoppingCartId, productPriceId, quantity); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs deleted file mode 100644 index 81e8bac..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/AddPackageToShoppingCartCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class AddPackageToShoppingCartCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - public Guid PackageId { get; set; } - - private AddPackageToShoppingCartCommand(Guid shoppingCartId, Guid packageId) - { - ShoppingCartId = shoppingCartId; - PackageId = packageId; - } - - public static AddPackageToShoppingCartCommand Create(Guid shoppingCartId, Guid packageId) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId)); - - if (packageId == Guid.Empty) - throw new ArgumentException($"Package ID is required", nameof(packageId)); - - return new(shoppingCartId, packageId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs deleted file mode 100644 index fdc7b0e..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/CreateShoppingCartCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class CreateShoppingCartCommand : IRequest> -{ - public Guid? CustomerId { get; set; } - - public Guid? OrderId { get; set; } - - public Guid? QuoteId { get; set; } - - private CreateShoppingCartCommand(Guid customerId, Guid? orderId = null, Guid? quoteId = null) - { - CustomerId = customerId; - OrderId = orderId; - QuoteId = quoteId; - } - - public static CreateShoppingCartCommand Create(Guid customerId, Guid? orderId = null, Guid? quoteId = null) - { - if (customerId == Guid.Empty) - throw new ArgumentException($"Customer ID is required", nameof(customerId)); - - return new(customerId, orderId, quoteId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs deleted file mode 100644 index f535ca4..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/EmptyShoppingCartCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class EmptyShoppingCartCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - private EmptyShoppingCartCommand(Guid shoppingCartId) => ShoppingCartId = shoppingCartId; - - public static EmptyShoppingCartCommand Create(Guid shoppingCartId) - { - if(shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required.", nameof(shoppingCartId)); - - return new(shoppingCartId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs deleted file mode 100644 index 7a2cd2c..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddItemToShoppingCartCommandHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class AddItemToShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(AddItemToShoppingCartCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ProductPrices.AnyAsync(c => c.Id == request.ProductPriceId, cancellationToken)) - return Result.Fail($"Product item could not be found with id {request.ProductPriceId}"); - - var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken); - - if (cart is null) - return Result.Fail($"Shopping cart could not be found with id {request.ShoppingCartId}"); - - if (cart.ShoppingCartItems?.Any(i => i.ProductPriceId == request.ProductPriceId) == true) - return Result.Fail($"Item already in shopping cart with id {request.ShoppingCartId}"); - - context.ShoppingCartItems.Add(new Entities.ShoppingCartItem - { - ShoppingCartId = request.ShoppingCartId, - ProductPriceId = request.ProductPriceId, - Quantity = request.Quantity - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to add cart item with id {request.ProductPriceId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs deleted file mode 100644 index a634375..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/AddPackageToShoppingCartCommandHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class AddPackageToShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(AddPackageToShoppingCartCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Packages.AnyAsync(p => p.Id == request.PackageId, cancellationToken)) - return Result.Fail($"Package cold not be found by ID {request.PackageId}"); - - var shoppingCart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken); - - if (shoppingCart is null) - return Result.Fail($"Shopping cart could not be found by ID {request.ShoppingCartId}"); - - if (!await context.ShoppingCartPackages.AnyAsync(cp => cp.ShoppingCartId == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Package {request.PackageId} is already in the cart"); - - var newShoppingCartPackage = context.ShoppingCartPackages.Add(new Entities.ShoppingCartPackage - { - ShoppingCartId = request.ShoppingCartId, - PackageId = request.PackageId - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Could not add package of id {request.PackageId} to shopping cart {request.ShoppingCartId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs deleted file mode 100644 index 2d51f2e..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/CreateShoppingCartCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class CreateShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(CreateShoppingCartCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.Customers.AnyAsync(c => c.Id == request.CustomerId, cancellationToken)) - return Result.Fail($"Customer could not be found with id {request.CustomerId}"); - - var cart = context.ShoppingCarts.Add(new Entities.ShoppingCart - { - CustomerId = request.CustomerId, - OrderId = request.OrderId, - QuoteId = request.QuoteId - }); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok(cart.Entity.Id) - : Result.Fail($"Failed to create shopping cart for customer id {request.CustomerId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs deleted file mode 100644 index 0043a5f..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/EmptyShoppingCartCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class EmptyShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(EmptyShoppingCartCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Shopping could not be found with id {request.ShoppingCartId}"); - - if (await context.ShoppingCartItems.CountAsync(i => i.ShoppingCartId == request.ShoppingCartId, cancellationToken) == 0) - return Result.Ok(); - - var cartItems = await context.ShoppingCartItems.Where(i => i.ShoppingCartId == request.ShoppingCartId).ToListAsync(cancellationToken); - - context.RemoveRange(cartItems); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Could not empty cart with id {request.ShoppingCartId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs deleted file mode 100644 index 2bd5b3a..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemovePackageFromShoppingCartCommandHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class RemovePackageFromShoppingCartCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(RemovePackageFromShoppingCartCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Shopping cart could not be found by ID {request.ShoppingCartId}"); - - if (!await context.ShoppingCartPackages.AnyAsync(p => p.Id == request.ShoppingCartPackageId, cancellationToken)) - return Result.Fail($"Shopping cart package {request.ShoppingCartPackageId} is not in the shopping cart {request.ShoppingCartId}"); - - var shoppingCartPackage = await context.ShoppingCartPackages.FirstOrDefaultAsync(cp => cp.Id == request.ShoppingCartPackageId, cancellationToken); - - if (shoppingCartPackage is null) - return Result.Ok(); - - context.ShoppingCartPackages.Remove(shoppingCartPackage!); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Could remove package of id {request.ShoppingCartPackageId} from shopping cart {request.ShoppingCartId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs deleted file mode 100644 index 442c944..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/RemoveShoppingCartItemCommandHandler.cs +++ /dev/null @@ -1,36 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class RemoveShoppingCartItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(RemoveShoppingCartItemCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ProductPrices.AnyAsync(c => c.Id == request.ShoppingCartItemId, cancellationToken)) - return Result.Fail($"Product item could not be found with id {request.ShoppingCartItemId}"); - - var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == request.ShoppingCartId, cancellationToken); - - if (cart is null) - return Result.Fail($"Shopping cart item could not be found with id {request.ShoppingCartId}"); - - var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.Id == request.ShoppingCartItemId, cancellationToken); - - if (item is null) return Result.Ok(); - - context.ShoppingCartItems.Remove(item); - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to remove shopping cart item with id {request.ShoppingCartItemId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs deleted file mode 100644 index 3e54242..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/Handlers/UpdateShoppingCartItemCommandHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Features.Shop.Postgres; - -namespace LiteCharms.Features.ShoppingCarts.Commands.Handlers; - -public class UpdateShoppingCartItemCommandHandler(IDbContextFactory contextFactory) : IRequestHandler -{ - public async ValueTask Handle(UpdateShoppingCartItemCommand request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Shopping could not be found with id {request.ShoppingCartId}"); - - var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.ShoppingCartId == request.ShoppingCartId, cancellationToken); - - if(item is null) - return Result.Fail($"Shopping cart item could not be found with id {request.ShoppingCartItemId}"); - - item.Quantity = request.Quantity; - - return await context.SaveChangesAsync(cancellationToken) > 0 - ? Result.Ok() - : Result.Fail($"Failed to update cart item quntity"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs deleted file mode 100644 index 6aa8f25..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemovePackageFromShoppingCartCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class RemovePackageFromShoppingCartCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - public Guid ShoppingCartPackageId { get; set; } - - private RemovePackageFromShoppingCartCommand(Guid shoppingCartId, Guid shoppingCartPackageId) - { - ShoppingCartId = shoppingCartId; - ShoppingCartPackageId = shoppingCartPackageId; - } - - public static RemovePackageFromShoppingCartCommand Create(Guid shoppingCartId, Guid shoppingCartPackageId) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId)); - - if (shoppingCartPackageId == Guid.Empty) - throw new ArgumentException($"Shopping cart Package ID is required", nameof(shoppingCartPackageId)); - - return new(shoppingCartId, shoppingCartPackageId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs deleted file mode 100644 index 26706d8..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/RemoveShoppingCartItemCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class RemoveShoppingCartItemCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - public Guid ShoppingCartItemId { get; set; } - - private RemoveShoppingCartItemCommand(Guid shoppingCartId, Guid shoppingCartItemId) - { - ShoppingCartId = shoppingCartId; - ShoppingCartItemId = shoppingCartItemId; - } - - public static RemoveShoppingCartItemCommand Create(Guid shoppingCartId, Guid shoppingCartItemId) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId)); - - if (shoppingCartItemId == Guid.Empty) - throw new ArgumentException($"Shopping cart item required", nameof(shoppingCartItemId)); - - return new(shoppingCartId, shoppingCartItemId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs b/LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs deleted file mode 100644 index d7cebe0..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Commands/UpdateShoppingCartItemCommand.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace LiteCharms.Features.ShoppingCarts.Commands; - -public class UpdateShoppingCartItemCommand : IRequest -{ - public Guid ShoppingCartId { get; set; } - - public Guid ShoppingCartItemId { get; set; } - - public int Quantity { get; set; } - - private UpdateShoppingCartItemCommand(Guid shoppingCartId, Guid shoppingCartItemId, int quantity = 1) - { - ShoppingCartId = shoppingCartId; - ShoppingCartItemId = shoppingCartItemId; - Quantity = quantity; - } - - public static UpdateShoppingCartItemCommand Create(Guid shoppingCartId, Guid shoppingCartItemId, int quantity = 1) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException($"Shopping cart ID is required", nameof(shoppingCartId)); - - if (shoppingCartItemId == Guid.Empty) - throw new ArgumentException($"Shopping cart item is required", nameof(shoppingCartItemId)); - - if (quantity <= 0) throw new ArgumentException($"Quantity must be at least 1", nameof(quantity)); - - return new(shoppingCartId, shoppingCartItemId, quantity); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs deleted file mode 100644 index e901ee3..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetCustomerShoppingCartsQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.ShoppingCarts.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/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs deleted file mode 100644 index e6ddec3..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartItemsQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.ShoppingCarts.Models; - -namespace LiteCharms.Features.ShoppingCarts.Queries; - -public class GetShoppingCartItemsQuery : IRequest> -{ - public Guid ShoppingCartId { get; set; } - - private GetShoppingCartItemsQuery(Guid shoppingCartId) => ShoppingCartId = shoppingCartId; - - public static GetShoppingCartItemsQuery Create(Guid shoppingCartId) - { - if (shoppingCartId == Guid.Empty) - throw new ArgumentException("Shopping cart id is required", nameof(shoppingCartId)); - - return new(shoppingCartId); - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs deleted file mode 100644 index 611b48f..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartPackagesQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.ShoppingCarts.Models; - -namespace LiteCharms.Features.ShoppingCarts.Queries; - -public class GetShoppingCartPackagesQuery : IRequest> -{ - public Guid ShoppingCartId { get; set; } - - private GetShoppingCartPackagesQuery(Guid shoppingCartId) => ShoppingCartId = shoppingCartId; - - public static GetShoppingCartPackagesQuery 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/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs deleted file mode 100644 index 1ef9784..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/GetShoppingCartQuery.cs +++ /dev/null @@ -1,18 +0,0 @@ -using LiteCharms.Features.Shop.ShoppingCarts.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/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs deleted file mode 100644 index 8554fc6..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetCustomerShoppingCartsQueryHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.ShoppingCarts.Models; -using LiteCharms.Features.ShoppingCarts.Queries; - -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/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs deleted file mode 100644 index 0082f85..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartItemsQueryHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.ShoppingCarts.Models; - -namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; - -public class GetShoppingCartItemsQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetShoppingCartItemsQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ShoppingCarts.AnyAsync(i => i.Id == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Shopping cart could not be found with id {request.ShoppingCartId}"); - - var items = await context.ShoppingCartItems.AsNoTracking() - .Where(i => i.ShoppingCartId == request.ShoppingCartId).ToArrayAsync(cancellationToken); - - return items?.Length > 0 - ? Result.Ok(items.Select(i => i.ToModel()).ToArray()) - : Result.Fail($"Failed to retrieve shopping cart items with id {request.ShoppingCartId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs deleted file mode 100644 index 68e17cf..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartPackagesQueryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.ShoppingCarts.Models; - -namespace LiteCharms.Features.ShoppingCarts.Queries.Handlers; - -public class GetShoppingCartPackagesQueryHandler(IDbContextFactory contextFactory) : IRequestHandler> -{ - public async ValueTask> Handle(GetShoppingCartPackagesQuery request, CancellationToken cancellationToken) - { - try - { - using var context = await contextFactory.CreateDbContextAsync(cancellationToken); - - if (!await context.ShoppingCarts.AnyAsync(c => c.Id == request.ShoppingCartId, cancellationToken)) - return Result.Fail($"Shopping cart could not be found by ID {request.ShoppingCartId}"); - - var packages = await context.ShoppingCartPackages.AsNoTracking() - .OrderByDescending(o => o.CreatedAt) - .Where(cp => cp.ShoppingCartId == request.ShoppingCartId) - .ToArrayAsync(cancellationToken); - - return packages?.Length > 0 - ? Result.Ok(packages.Select(p => p.ToModel()).ToArray()) - : Result.Fail($"Could not find packaged in shopping cart by ID {request.ShoppingCartId}"); - } - catch (Exception ex) - { - return Result.Fail(new Error(ex.Message).CausedBy(ex)); - } - } -} diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs b/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs deleted file mode 100644 index 2cf05a9..0000000 --- a/LiteCharms.Features/Shop/ShoppingCarts/Queries/Handlers/GetShoppingCartQueryHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using LiteCharms.Extensions; -using LiteCharms.Features.Shop.Postgres; -using LiteCharms.Features.Shop.ShoppingCarts.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.Features/Shop/ShoppingCarts/ShoppingCartService.cs b/LiteCharms.Features/Shop/ShoppingCarts/ShoppingCartService.cs new file mode 100644 index 0000000..b4fdd2f --- /dev/null +++ b/LiteCharms.Features/Shop/ShoppingCarts/ShoppingCartService.cs @@ -0,0 +1,298 @@ +using LiteCharms.Features.Extensions; +using LiteCharms.Features.Shop.Postgres; +using LiteCharms.Features.Shop.ShoppingCarts.Models; +using static LiteCharms.Features.Extensions.Timezones; + +namespace LiteCharms.Features.Shop.ShoppingCarts; + +public class ShoppingCartService(IDbContextFactory contextFactory) +{ + public async ValueTask AddItemToShoppingCartAsync(Guid shoppingCartId, Guid productPriceId, int quantity, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ProductPrices.AnyAsync(c => c.Id == productPriceId, cancellationToken)) + return Result.Fail($"Product item could not be found with id {productPriceId}"); + + var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); + + if (cart is null) + return Result.Fail($"Shopping cart could not be found with id {shoppingCartId}"); + + if (cart.ShoppingCartItems?.Any(i => i.ProductPriceId == productPriceId) == true) + return Result.Fail($"Item already in shopping cart with id {shoppingCartId}"); + + context.ShoppingCartItems.Add(new Entities.ShoppingCartItem + { + ShoppingCartId = shoppingCartId, + ProductPriceId = productPriceId, + Quantity = quantity + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to add cart item with id {productPriceId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask AddPackageToShoppingCartAsync(Guid shoppingCartId, Guid packageId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Packages.AnyAsync(p => p.Id == packageId, cancellationToken)) + return Result.Fail($"Package cold not be found by ID {packageId}"); + + var shoppingCart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); + + if (shoppingCart is null) + return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); + + if (!await context.ShoppingCartPackages.AnyAsync(cp => cp.ShoppingCartId == shoppingCartId, cancellationToken)) + return Result.Fail($"Package {packageId} is already in the cart"); + + var newShoppingCartPackage = context.ShoppingCartPackages.Add(new Entities.ShoppingCartPackage + { + ShoppingCartId = shoppingCartId, + PackageId = packageId + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Could not add package of id {packageId} to shopping cart {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> CreateShoppingCartAsync(Guid customerId, Guid orderId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail($"Customer could not be found with id {customerId}"); + + var cart = context.ShoppingCarts.Add(new Entities.ShoppingCart + { + CustomerId = customerId, + OrderId = orderId + }); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok(cart.Entity.Id) + : Result.Fail($"Failed to create shopping cart for customer id {customerId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask EmptyShoppingCartAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) + return Result.Fail($"Shopping could not be found with id {shoppingCartId}"); + + if (await context.ShoppingCartItems.CountAsync(i => i.ShoppingCartId == shoppingCartId, cancellationToken) == 0) + return Result.Ok(); + + var cartItems = await context.ShoppingCartItems.Where(i => i.ShoppingCartId == shoppingCartId).ToListAsync(cancellationToken); + + context.RemoveRange(cartItems); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Could not empty cart with id {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetCustomerShoppingCartsAsync(Guid customerId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.Customers.AnyAsync(c => c.Id == customerId, cancellationToken)) + return Result.Fail(new Error($"Customer with Id {customerId} does not exist.")); + + var shoppingCarts = await context.ShoppingCarts.Where(sc => sc.CustomerId == 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 {customerId}.")); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetShoppingCartAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + var cart = await context.ShoppingCarts.AsNoTracking().FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); + + return cart is not null + ? Result.Ok(cart.ToModel()) + : Result.Fail($"Failed to find shopping cart with id {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetShoppingCartItemsAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ShoppingCarts.AnyAsync(i => i.Id == shoppingCartId, cancellationToken)) + return Result.Fail($"Shopping cart could not be found with id {shoppingCartId}"); + + var items = await context.ShoppingCartItems.AsNoTracking() + .Where(i => i.ShoppingCartId == shoppingCartId).ToArrayAsync(cancellationToken); + + return items?.Length > 0 + ? Result.Ok(items.Select(i => i.ToModel()).ToArray()) + : Result.Fail($"Failed to retrieve shopping cart items with id {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask> GetShoppingCartPackagesAsync(Guid shoppingCartId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) + return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); + + var packages = await context.ShoppingCartPackages.AsNoTracking() + .OrderByDescending(o => o.CreatedAt) + .Where(cp => cp.ShoppingCartId == shoppingCartId) + .ToArrayAsync(cancellationToken); + + return packages?.Length > 0 + ? Result.Ok(packages.Select(p => p.ToModel()).ToArray()) + : Result.Fail($"Could not find packaged in shopping cart by ID {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask RemovePackageFromShoppingCartAsync(Guid shoppingCartId, Guid shoppingCartPackageId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) + return Result.Fail($"Shopping cart could not be found by ID {shoppingCartId}"); + + if (!await context.ShoppingCartPackages.AnyAsync(p => p.Id == shoppingCartPackageId, cancellationToken)) + return Result.Fail($"Shopping cart package {shoppingCartPackageId} is not in the shopping cart {shoppingCartId}"); + + var shoppingCartPackage = await context.ShoppingCartPackages.FirstOrDefaultAsync(cp => cp.Id == shoppingCartPackageId, cancellationToken); + + if (shoppingCartPackage is null) + return Result.Ok(); + + context.ShoppingCartPackages.Remove(shoppingCartPackage!); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Could remove package of id {shoppingCartPackageId} from shopping cart {shoppingCartId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask RemoveShoppingCartItemAsync(Guid shoppingCartId, Guid shoppingCartItemId, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ProductPrices.AnyAsync(c => c.Id == shoppingCartItemId, cancellationToken)) + return Result.Fail($"Product item could not be found with id {shoppingCartItemId}"); + + var cart = await context.ShoppingCarts.FirstOrDefaultAsync(c => c.Id == shoppingCartId, cancellationToken); + + if (cart is null) + return Result.Fail($"Shopping cart item could not be found with id {shoppingCartId}"); + + var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.Id == shoppingCartItemId, cancellationToken); + + if (item is null) return Result.Ok(); + + context.ShoppingCartItems.Remove(item); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to remove shopping cart item with id {shoppingCartItemId}"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } + + public async ValueTask UpdateShoppingCartItemAsync(Guid shoppingCartId, Guid shoppingCartItemId, int quantity, CancellationToken cancellationToken = default) + { + try + { + using var context = await contextFactory.CreateDbContextAsync(cancellationToken); + + if (!await context.ShoppingCarts.AnyAsync(c => c.Id == shoppingCartId, cancellationToken)) + return Result.Fail($"Shopping could not be found with id {shoppingCartId}"); + + var item = await context.ShoppingCartItems.FirstOrDefaultAsync(i => i.ShoppingCartId == shoppingCartId, cancellationToken); + + if (item is null) + return Result.Fail($"Shopping cart item could not be found with id {shoppingCartItemId}"); + + item.Quantity = quantity; + item.UpdatedAt = SouthAfricanTimeZone.UtcNow(); + + return await context.SaveChangesAsync(cancellationToken) > 0 + ? Result.Ok() + : Result.Fail($"Failed to update cart item quntity"); + } + catch (Exception ex) + { + return Result.Fail(new Error(ex.Message).CausedBy(ex)); + } + } +} -- 2.47.3 From 2610275bef3cab138f32e7cd37b1e6baea71aa98 Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Thu, 14 May 2026 02:46:07 +0200 Subject: [PATCH 4/5] Stable run on Notification creation --- LiteCharms.Features/Extensions/Timezones.cs | 2 +- .../LiteCharms.Features.csproj | 9 + .../Entities/PackageConfirguration.cs | 2 +- .../Shop/CartPackages/Models/Package.cs | 4 +- .../Entities/CustomerConfiguration.cs | 4 +- .../Shop/Customers/Models/Customer.cs | 4 +- .../Shop/Leads/Entities/LeadConfiguration.cs | 11 +- LiteCharms.Features/Shop/Leads/Models/Lead.cs | 4 +- .../Entities/NotificationConfiguration.cs | 4 +- .../Shop/Notifications/Models/Notification.cs | 4 +- .../Shop/Notifications/NotificationService.cs | 2 +- .../Orders/Entities/OrderConfiguration.cs | 4 +- .../Shop/Orders/Entities/OrderRefund.cs | 1 + .../Entities/OrderRefundConfiguration.cs | 2 +- .../Shop/Orders/Models/Order.cs | 4 +- .../Shop/Orders/Models/OrderRefund.cs | 2 +- ...514004002_UsedStringTableNames.Designer.cs | 871 ++++++++++++++++ .../20260514004002_UsedStringTableNames.cs | 936 ++++++++++++++++++ .../Migrations/ShopDbContextModelSnapshot.cs | 458 +++++---- .../Shop/Postgres/ShopDbContext.cs | 30 + .../Shop/Products/Entities/Product.cs | 1 + .../Products/Entities/ProductConfiguration.cs | 2 +- .../Entities/ProductPriceConfiguration.cs | 2 +- .../Shop/Products/Models/ProductPrice.cs | 4 +- .../Quotes/Entities/QuoteConfiguration.cs | 6 +- .../Shop/Quotes/Models/Quote.cs | 6 +- .../Entities/ShoppingCartConfiguration.cs | 4 +- .../Entities/ShoppingCartItem.cs | 1 + .../Entities/ShoppingCartItemConfiguration.cs | 4 +- .../ShoppingCartPackageConfiguration.cs | 2 +- .../Shop/ShoppingCarts/Models/ShoppingCart.cs | 4 +- .../ShoppingCarts/Models/ShoppingCartItem.cs | 4 +- .../Models/ShoppingCartPackage.cs | 2 +- LiteCharms.Features/appsettings.json | 22 + 34 files changed, 2199 insertions(+), 223 deletions(-) create mode 100644 LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.Designer.cs create mode 100644 LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.cs create mode 100644 LiteCharms.Features/appsettings.json diff --git a/LiteCharms.Features/Extensions/Timezones.cs b/LiteCharms.Features/Extensions/Timezones.cs index bbddd5d..3976240 100644 --- a/LiteCharms.Features/Extensions/Timezones.cs +++ b/LiteCharms.Features/Extensions/Timezones.cs @@ -23,5 +23,5 @@ public static class Timezones return DateTimeOffset.Parse(localised!); } - public static DateTimeOffset UtcNow(this TimeZoneInfo timezone) => ToDateTimeWithTimeZone(DateTime.Now, timezone); + public static DateTime UtcNow(this TimeZoneInfo timezone) => ToDateTimeWithTimeZone(DateTime.Now, timezone).UtcDateTime; } diff --git a/LiteCharms.Features/LiteCharms.Features.csproj b/LiteCharms.Features/LiteCharms.Features.csproj index 9ad7508..a9aedad 100644 --- a/LiteCharms.Features/LiteCharms.Features.csproj +++ b/LiteCharms.Features/LiteCharms.Features.csproj @@ -21,6 +21,7 @@ LICENSE utility;dotnet icon.png + 8a78916e-c86b-4f4b-9f4e-d8e7769b5d23 @@ -143,5 +144,13 @@ + + + PreserveNewest + + + + + diff --git a/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs b/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs index e6ff029..cfc89ff 100644 --- a/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs +++ b/LiteCharms.Features/Shop/CartPackages/Entities/PackageConfirguration.cs @@ -8,7 +8,7 @@ public class PackageConfirguration : IEntityTypeConfiguration builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.Name).IsRequired(); builder.Property(f => f.Summary).IsRequired().HasMaxLength(512); builder.Property(f => f.Description).IsRequired().HasMaxLength(2048); diff --git a/LiteCharms.Features/Shop/CartPackages/Models/Package.cs b/LiteCharms.Features/Shop/CartPackages/Models/Package.cs index fcc560b..9d2e8b0 100644 --- a/LiteCharms.Features/Shop/CartPackages/Models/Package.cs +++ b/LiteCharms.Features/Shop/CartPackages/Models/Package.cs @@ -4,9 +4,9 @@ public class Package { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public string? Name { get; set; } diff --git a/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs b/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs index 335c1b0..f5ce014 100644 --- a/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs +++ b/LiteCharms.Features/Shop/Customers/Entities/CustomerConfiguration.cs @@ -4,11 +4,11 @@ public class CustomerConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Customer)); + builder.ToTable("Customers"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.Company); builder.Property(f => f.Name).IsRequired(); builder.Property(f => f.LastName).IsRequired(); diff --git a/LiteCharms.Features/Shop/Customers/Models/Customer.cs b/LiteCharms.Features/Shop/Customers/Models/Customer.cs index 14387f6..d4cec8c 100644 --- a/LiteCharms.Features/Shop/Customers/Models/Customer.cs +++ b/LiteCharms.Features/Shop/Customers/Models/Customer.cs @@ -4,9 +4,9 @@ public class Customer { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public string? Company { get; set; } diff --git a/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs b/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs index 482677c..39a0a20 100644 --- a/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs +++ b/LiteCharms.Features/Shop/Leads/Entities/LeadConfiguration.cs @@ -4,12 +4,12 @@ public class LeadConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Lead)); + builder.ToTable("Leads"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.CustomerId).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); + builder.Property(f => f.CustomerId); builder.Property(f => f.Source); builder.Property(f => f.ClickId); builder.Property(f => f.WebClickId); @@ -22,5 +22,10 @@ public class LeadConfiguration : IEntityTypeConfiguration builder.Property(f => f.ClickLocation); builder.Property(f => f.Status).IsRequired(); builder.Property(f => f.AttributionHash).IsRequired(true); + + builder.HasOne(f => f.Customer) + .WithMany() + .HasForeignKey(f => f.CustomerId) + .OnDelete(DeleteBehavior.Restrict); } } diff --git a/LiteCharms.Features/Shop/Leads/Models/Lead.cs b/LiteCharms.Features/Shop/Leads/Models/Lead.cs index 0cb9fa1..adfb1ba 100644 --- a/LiteCharms.Features/Shop/Leads/Models/Lead.cs +++ b/LiteCharms.Features/Shop/Leads/Models/Lead.cs @@ -4,9 +4,9 @@ public class Lead { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public Guid? CustomerId { get; set; } diff --git a/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs index 8a00b68..7139d0d 100644 --- a/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs +++ b/LiteCharms.Features/Shop/Notifications/Entities/NotificationConfiguration.cs @@ -4,11 +4,11 @@ public class NotificationConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Notification)); + builder.ToTable("Notification"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.Direction).IsRequired().HasConversion(); builder.Property(f => f.Platform).IsRequired().HasConversion(); builder.Property(f => f.Priority).IsRequired().HasConversion(); diff --git a/LiteCharms.Features/Shop/Notifications/Models/Notification.cs b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs index bb9b77c..fd085a1 100644 --- a/LiteCharms.Features/Shop/Notifications/Models/Notification.cs +++ b/LiteCharms.Features/Shop/Notifications/Models/Notification.cs @@ -4,9 +4,9 @@ public class Notification { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public NotificationDirection Direction { get; set; } diff --git a/LiteCharms.Features/Shop/Notifications/NotificationService.cs b/LiteCharms.Features/Shop/Notifications/NotificationService.cs index 84e7565..59493e4 100644 --- a/LiteCharms.Features/Shop/Notifications/NotificationService.cs +++ b/LiteCharms.Features/Shop/Notifications/NotificationService.cs @@ -31,7 +31,7 @@ public class NotificationService(IDbContextFactory contextFactory Processed = false }); - return newNotification is not null + return await context.SaveChangesAsync(cancellationToken) > 0 ? Result.Ok(newNotification.Entity.Id) : Result.Fail(new Error("Failed to create notification")); } diff --git a/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs index d79fb0e..848aaed 100644 --- a/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderConfiguration.cs @@ -4,11 +4,11 @@ public class OrderConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Order)); + builder.ToTable("Orders"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.CustomerId).IsRequired(); builder.Property(f => f.Status).HasConversion().IsRequired(); builder.Property(f => f.Requirements).HasColumnType("jsonb").IsRequired(false); diff --git a/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs index 33591d7..8f7e462 100644 --- a/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderRefund.cs @@ -3,5 +3,6 @@ [EntityTypeConfiguration] public class OrderRefund : Models.OrderRefund { + public virtual Order? Order { get; set; } } diff --git a/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs b/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs index f353123..b33c2ee 100644 --- a/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs +++ b/LiteCharms.Features/Shop/Orders/Entities/OrderRefundConfiguration.cs @@ -4,7 +4,7 @@ public class OrderRefundConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(OrderRefund)); + builder.ToTable("OrderRefunds"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); diff --git a/LiteCharms.Features/Shop/Orders/Models/Order.cs b/LiteCharms.Features/Shop/Orders/Models/Order.cs index d093993..30bb75d 100644 --- a/LiteCharms.Features/Shop/Orders/Models/Order.cs +++ b/LiteCharms.Features/Shop/Orders/Models/Order.cs @@ -4,9 +4,9 @@ public class Order { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public Guid CustomerId { get; set; } diff --git a/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs b/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs index 34668a7..1463afd 100644 --- a/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs +++ b/LiteCharms.Features/Shop/Orders/Models/OrderRefund.cs @@ -4,7 +4,7 @@ public class OrderRefund { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } public Guid OrderId { get; set; } diff --git a/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.Designer.cs b/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.Designer.cs new file mode 100644 index 0000000..b0185a2 --- /dev/null +++ b/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.Designer.cs @@ -0,0 +1,871 @@ +// +using System; +using LiteCharms.Features.Shop.Postgres; +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.Features.Shop.Postgres.Migrations +{ + [DbContext(typeof(ShopDbContext))] + [Migration("20260514004002_UsedStringTableNames")] + partial class UsedStringTableNames + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.8") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Package", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("PackageId") + .HasColumnType("uuid"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("PackageItem", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.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") + .HasDefaultValueSql("now()"); + + 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") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customers", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Discord") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LinkedIn") + .HasColumnType("text"); + + b.Property("Name") + .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") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Leads.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") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("CustomerId1") + .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") + .HasColumnType("timestamp with time zone"); + + b.Property("WebClickId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("CustomerId1"); + + b.ToTable("Leads", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Notifications.Entities.Notification", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .IsRequired() + .HasColumnType("text"); + + b.Property("CorrelationIdType") + .HasColumnType("integer"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Direction") + .HasColumnType("integer"); + + b.PrimitiveCollection("Errors") + .HasColumnType("jsonb"); + + b.Property("HasError") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsHtml") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("IsInternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Message") + .IsRequired() + .HasColumnType("text"); + + b.Property("Platform") + .HasColumnType("integer"); + + b.Property("Priority") + .HasColumnType("integer"); + + b.Property("Processed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("RecipientAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("RecipientName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SenderAddress") + .IsRequired() + .HasColumnType("text"); + + b.Property("SenderName") + .HasColumnType("text"); + + b.Property("Subject") + .IsRequired() + .HasColumnType("text"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Notification", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.PrimitiveCollection("Notes") + .HasColumnType("jsonb"); + + b.PrimitiveCollection("Requirements") + .HasColumnType("jsonb"); + + b.Property("Status") + .HasColumnType("integer"); + + b.PrimitiveCollection("Terms") + .HasColumnType("jsonb"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.ToTable("Orders", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.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") + .HasDefaultValueSql("now()"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OrderId"); + + b.ToTable("OrderRefunds", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.PrimitiveCollection("Thumbnails") + .HasColumnType("jsonb"); + + b.HasKey("Id"); + + b.ToTable("Products", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + 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") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductId"); + + b.ToTable("ProductPrices", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("ExpiredAt") + .HasColumnType("timestamp with time zone"); + + b.Property("InvoiceUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("Reason") + .HasColumnType("text"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.HasIndex("ShoppingCartId") + .IsUnique(); + + b.ToTable("Quotes", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("CustomerId") + .HasColumnType("uuid"); + + b.Property("OrderId") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("OrderId") + .IsUnique(); + + b.ToTable("ShoppingCarts", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.Property("ProductPriceId1") + .HasColumnType("uuid"); + + b.Property("Quantity") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.Property("ShoppingCartId1") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.HasIndex("ProductPriceId"); + + b.HasIndex("ProductPriceId1"); + + b.HasIndex("ShoppingCartId"); + + b.HasIndex("ShoppingCartId1"); + + b.ToTable("ShoppingCartItems", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("PackageId") + .HasColumnType("uuid"); + + b.Property("ShoppingCartId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("ShoppingCartId"); + + b.ToTable("ShoppingCartPackages", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b => + { + b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package") + .WithMany("PackageItems") + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("ProductPrice"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b => + { + b.HasOne("LiteCharms.Features.Shop.Customers.Models.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", null) + .WithMany("Leads") + .HasForeignKey("CustomerId1"); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => + { + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b => + { + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") + .WithMany("Refunds") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.Shop.Products.Entities.Product", "Product") + .WithMany("ProductPrices") + .HasForeignKey("ProductId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Product"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b => + { + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") + .WithMany("Quotes") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "OrderId"); + + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") + .WithOne("Quote") + .HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "ShoppingCartId"); + + b.Navigation("Customer"); + + b.Navigation("Order"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => + { + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") + .WithMany("ShoppingCarts") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") + .WithOne("ShoppingCart") + .HasForeignKey("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "OrderId") + .OnDelete(DeleteBehavior.SetNull); + + b.Navigation("Customer"); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b => + { + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", null) + .WithMany() + .HasForeignKey("ProductPriceId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId1"); + + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", null) + .WithMany("ShoppingCartItems") + .HasForeignKey("ShoppingCartId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") + .WithMany() + .HasForeignKey("ShoppingCartId1"); + + b.Navigation("ProductPrice"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b => + { + b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package") + .WithMany() + .HasForeignKey("PackageId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") + .WithMany("ShoppingCartPackages") + .HasForeignKey("ShoppingCartId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Package"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b => + { + b.Navigation("PackageItems"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b => + { + b.Navigation("Leads"); + + b.Navigation("Orders"); + + b.Navigation("Quotes"); + + b.Navigation("ShoppingCarts"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => + { + b.Navigation("Quote"); + + b.Navigation("Refunds"); + + b.Navigation("ShoppingCart"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b => + { + b.Navigation("ProductPrices"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => + { + b.Navigation("Quote"); + + b.Navigation("ShoppingCartItems"); + + b.Navigation("ShoppingCartPackages"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.cs b/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.cs new file mode 100644 index 0000000..234f04a --- /dev/null +++ b/LiteCharms.Features/Shop/Postgres/Migrations/20260514004002_UsedStringTableNames.cs @@ -0,0 +1,936 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace LiteCharms.Features.Shop.Postgres.Migrations +{ + /// + public partial class UsedStringTableNames : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_PackageItem_ProductPrice_ProductPriceId", + table: "PackageItem"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ProductPrice_ProductPriceId", + table: "ShoppingCartItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ShoppingCart_ShoppingCartId", + table: "ShoppingCartItems"); + + migrationBuilder.DropTable( + name: "Lead"); + + migrationBuilder.DropTable( + name: "OrderRefund"); + + migrationBuilder.DropTable( + name: "ProductPrice"); + + migrationBuilder.DropTable( + name: "Quote"); + + migrationBuilder.DropTable( + name: "ShoppingCartPackage"); + + migrationBuilder.DropTable( + name: "Product"); + + migrationBuilder.DropTable( + name: "ShoppingCart"); + + migrationBuilder.DropTable( + name: "Order"); + + migrationBuilder.RenameColumn( + name: "Sender", + table: "Notification", + newName: "SenderAddress"); + + migrationBuilder.RenameColumn( + name: "Recipient", + table: "Notification", + newName: "RecipientName"); + + migrationBuilder.AlterColumn( + name: "UpdatedAt", + table: "ShoppingCartItems", + type: "timestamp with time zone", + nullable: true, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Quantity", + table: "ShoppingCartItems", + type: "integer", + nullable: false, + defaultValue: 1, + oldClrType: typeof(int), + oldType: "integer"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + table: "ShoppingCartItems", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone"); + + migrationBuilder.AddColumn( + name: "ProductPriceId1", + table: "ShoppingCartItems", + type: "uuid", + nullable: true); + + migrationBuilder.AddColumn( + name: "ShoppingCartId1", + table: "ShoppingCartItems", + type: "uuid", + nullable: true); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Customer", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Customer", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Customer", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + table: "Customer", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTimeOffset), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "Active", + table: "Customer", + type: "boolean", + nullable: false, + oldClrType: typeof(bool), + oldType: "boolean", + oldDefaultValue: true); + + migrationBuilder.CreateTable( + name: "Customers", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + Company = table.Column(type: "text", nullable: true), + Name = table.Column(type: "text", nullable: false), + LastName = table.Column(type: "text", nullable: false), + Tax = table.Column(type: "text", nullable: true), + Email = table.Column(type: "text", nullable: false), + Discord = table.Column(type: "text", nullable: true), + Slack = table.Column(type: "text", nullable: true), + LinkedIn = table.Column(type: "text", nullable: true), + Whatsapp = table.Column(type: "text", nullable: true), + Website = table.Column(type: "text", nullable: true), + Phone = table.Column(type: "text", nullable: true), + Address = table.Column(type: "text", nullable: true), + City = table.Column(type: "text", nullable: true), + Region = table.Column(type: "text", nullable: true), + Country = table.Column(type: "text", nullable: true), + PostalCode = table.Column(type: "text", nullable: true), + Active = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Customers", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Products", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false), + Summary = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Description = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: false), + ImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Thumbnails = table.Column(type: "jsonb", nullable: true), + Active = table.Column(type: "boolean", nullable: false, defaultValue: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Products", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Leads", + 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, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + CustomerId = table.Column(type: "uuid", nullable: true), + Source = table.Column(type: "text", nullable: true), + ClickId = table.Column(type: "text", nullable: true), + WebClickId = table.Column(type: "text", nullable: true), + AppClickId = table.Column(type: "text", nullable: true), + CampaignId = table.Column(type: "bigint", nullable: true), + AdGroupId = table.Column(type: "bigint", nullable: true), + AdName = table.Column(type: "bigint", nullable: true), + TargetId = table.Column(type: "bigint", nullable: true), + FeedItemId = table.Column(type: "bigint", nullable: true), + ClickLocation = table.Column(type: "text", nullable: true), + AttributionHash = table.Column(type: "text", nullable: false), + Status = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Leads", x => x.Id); + table.ForeignKey( + name: "FK_Leads_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_Leads_Customers_CustomerId1", + column: x => x.CustomerId1, + principalTable: "Customers", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Orders", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + CustomerId = table.Column(type: "uuid", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Requirements = table.Column(type: "jsonb", nullable: true), + Notes = table.Column(type: "jsonb", nullable: true), + Terms = table.Column(type: "jsonb", nullable: true), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Orders", x => x.Id); + table.ForeignKey( + name: "FK_Orders_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "ProductPrices", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + ProductId = table.Column(type: "uuid", nullable: false), + Price = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + Discount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + Active = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductPrices", x => x.Id); + table.ForeignKey( + name: "FK_ProductPrices_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "OrderRefunds", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + OrderId = table.Column(type: "uuid", nullable: false), + Reason = table.Column(type: "text", nullable: false), + Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderRefunds", x => x.Id); + table.ForeignKey( + name: "FK_OrderRefunds_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCarts", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + CustomerId = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ShoppingCarts", x => x.Id); + table.ForeignKey( + name: "FK_ShoppingCarts_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ShoppingCarts_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "Quotes", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + ExpiredAt = table.Column(type: "timestamp with time zone", nullable: true), + CustomerId = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: true), + ShoppingCartId = table.Column(type: "uuid", nullable: true), + Status = table.Column(type: "integer", nullable: false), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Reason = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Quotes", x => x.Id); + table.ForeignKey( + name: "FK_Quotes_Customers_CustomerId", + column: x => x.CustomerId, + principalTable: "Customers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Quotes_Orders_OrderId", + column: x => x.OrderId, + principalTable: "Orders", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Quotes_ShoppingCarts_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCarts", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCartPackages", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + ShoppingCartId = table.Column(type: "uuid", nullable: false), + PackageId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ShoppingCartPackages", x => x.Id); + table.ForeignKey( + name: "FK_ShoppingCartPackages_Package_PackageId", + column: x => x.PackageId, + principalTable: "Package", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ShoppingCartPackages_ShoppingCarts_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCarts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartItems_ProductPriceId1", + table: "ShoppingCartItems", + column: "ProductPriceId1"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartItems_ShoppingCartId1", + table: "ShoppingCartItems", + column: "ShoppingCartId1"); + + migrationBuilder.CreateIndex( + name: "IX_Leads_CustomerId", + table: "Leads", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Leads_CustomerId1", + table: "Leads", + column: "CustomerId1"); + + migrationBuilder.CreateIndex( + name: "IX_OrderRefunds_OrderId", + table: "OrderRefunds", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_Orders_CustomerId", + table: "Orders", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_ProductPrices_ProductId", + table: "ProductPrices", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_Quotes_CustomerId", + table: "Quotes", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Quotes_OrderId", + table: "Quotes", + column: "OrderId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Quotes_ShoppingCartId", + table: "Quotes", + column: "ShoppingCartId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartPackages_PackageId", + table: "ShoppingCartPackages", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartPackages_ShoppingCartId", + table: "ShoppingCartPackages", + column: "ShoppingCartId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCarts_CustomerId", + table: "ShoppingCarts", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCarts_OrderId", + table: "ShoppingCarts", + column: "OrderId", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_PackageItem_ProductPrices_ProductPriceId", + table: "PackageItem", + column: "ProductPriceId", + principalTable: "ProductPrices", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId", + table: "ShoppingCartItems", + column: "ProductPriceId", + principalTable: "ProductPrices", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId1", + table: "ShoppingCartItems", + column: "ProductPriceId1", + principalTable: "ProductPrices", + principalColumn: "Id"); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId", + table: "ShoppingCartItems", + column: "ShoppingCartId", + principalTable: "ShoppingCarts", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId1", + table: "ShoppingCartItems", + column: "ShoppingCartId1", + principalTable: "ShoppingCarts", + principalColumn: "Id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_PackageItem_ProductPrices_ProductPriceId", + table: "PackageItem"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId", + table: "ShoppingCartItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ProductPrices_ProductPriceId1", + table: "ShoppingCartItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId", + table: "ShoppingCartItems"); + + migrationBuilder.DropForeignKey( + name: "FK_ShoppingCartItems_ShoppingCarts_ShoppingCartId1", + table: "ShoppingCartItems"); + + migrationBuilder.DropTable( + name: "Leads"); + + migrationBuilder.DropTable( + name: "OrderRefunds"); + + migrationBuilder.DropTable( + name: "ProductPrices"); + + migrationBuilder.DropTable( + name: "Quotes"); + + migrationBuilder.DropTable( + name: "ShoppingCartPackages"); + + migrationBuilder.DropTable( + name: "Products"); + + migrationBuilder.DropTable( + name: "ShoppingCarts"); + + migrationBuilder.DropTable( + name: "Orders"); + + migrationBuilder.DropTable( + name: "Customers"); + + migrationBuilder.DropIndex( + name: "IX_ShoppingCartItems_ProductPriceId1", + table: "ShoppingCartItems"); + + migrationBuilder.DropIndex( + name: "IX_ShoppingCartItems_ShoppingCartId1", + table: "ShoppingCartItems"); + + migrationBuilder.DropColumn( + name: "ProductPriceId1", + table: "ShoppingCartItems"); + + migrationBuilder.DropColumn( + name: "ShoppingCartId1", + table: "ShoppingCartItems"); + + migrationBuilder.RenameColumn( + name: "SenderAddress", + table: "Notification", + newName: "Sender"); + + migrationBuilder.RenameColumn( + name: "RecipientName", + table: "Notification", + newName: "Recipient"); + + migrationBuilder.AlterColumn( + name: "UpdatedAt", + table: "ShoppingCartItems", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)), + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Quantity", + table: "ShoppingCartItems", + type: "integer", + nullable: false, + oldClrType: typeof(int), + oldType: "integer", + oldDefaultValue: 1); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + table: "ShoppingCartItems", + type: "timestamp with time zone", + nullable: false, + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone", + oldDefaultValueSql: "now()"); + + migrationBuilder.AlterColumn( + name: "Name", + table: "Customer", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "LastName", + table: "Customer", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "Email", + table: "Customer", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CreatedAt", + table: "Customer", + type: "timestamp with time zone", + nullable: false, + defaultValueSql: "now()", + oldClrType: typeof(DateTime), + oldType: "timestamp with time zone"); + + migrationBuilder.AlterColumn( + name: "Active", + table: "Customer", + type: "boolean", + nullable: false, + defaultValue: true, + oldClrType: typeof(bool), + oldType: "boolean"); + + migrationBuilder.CreateTable( + name: "Lead", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: true), + AdGroupId = table.Column(type: "bigint", nullable: true), + AdName = table.Column(type: "bigint", nullable: true), + AppClickId = table.Column(type: "text", nullable: true), + AttributionHash = table.Column(type: "text", nullable: false), + CampaignId = table.Column(type: "bigint", nullable: true), + ClickId = table.Column(type: "text", nullable: true), + ClickLocation = table.Column(type: "text", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + FeedItemId = table.Column(type: "bigint", nullable: true), + Source = table.Column(type: "text", nullable: true), + Status = table.Column(type: "integer", nullable: false), + TargetId = table.Column(type: "bigint", nullable: true), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true), + WebClickId = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Lead", x => x.Id); + table.ForeignKey( + name: "FK_Lead_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "Order", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Notes = table.Column(type: "jsonb", nullable: true), + Requirements = table.Column(type: "jsonb", nullable: true), + Status = table.Column(type: "integer", nullable: false), + Terms = table.Column(type: "jsonb", nullable: true), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Order", x => x.Id); + table.ForeignKey( + name: "FK_Order_Customer_CustomerId", + column: x => x.CustomerId, + principalTable: "Customer", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Product", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Active = table.Column(type: "boolean", nullable: false, defaultValue: true), + Description = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: false), + ImageUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Name = table.Column(type: "text", nullable: false), + Summary = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Thumbnails = table.Column(type: "jsonb", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Product", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "OrderRefund", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: false), + Amount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + Reason = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_OrderRefund", x => x.Id); + table.ForeignKey( + name: "FK_OrderRefund_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCart", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + UpdatedAt = table.Column(type: "timestamp with time zone", 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", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_ShoppingCart_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id", + onDelete: ReferentialAction.SetNull); + }); + + migrationBuilder.CreateTable( + name: "ProductPrice", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ProductId = table.Column(type: "uuid", nullable: false), + Active = table.Column(type: "boolean", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + Discount = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + Price = table.Column(type: "numeric(18,2)", precision: 18, scale: 2, nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProductPrice", x => x.Id); + table.ForeignKey( + name: "FK_ProductPrice_Product_ProductId", + column: x => x.ProductId, + principalTable: "Product", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Quote", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CustomerId = table.Column(type: "uuid", nullable: false), + OrderId = table.Column(type: "uuid", nullable: true), + ShoppingCartId = table.Column(type: "uuid", nullable: true), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()"), + ExpiredAt = table.Column(type: "timestamp with time zone", nullable: true), + InvoiceUrl = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true), + Reason = table.Column(type: "text", nullable: true), + Status = table.Column(type: "integer", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", 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_Order_OrderId", + column: x => x.OrderId, + principalTable: "Order", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_Quote_ShoppingCart_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCart", + principalColumn: "Id"); + }); + + migrationBuilder.CreateTable( + name: "ShoppingCartPackage", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PackageId = table.Column(type: "uuid", nullable: false), + ShoppingCartId = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false, defaultValueSql: "now()") + }, + constraints: table => + { + table.PrimaryKey("PK_ShoppingCartPackage", x => x.Id); + table.ForeignKey( + name: "FK_ShoppingCartPackage_Package_PackageId", + column: x => x.PackageId, + principalTable: "Package", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + table.ForeignKey( + name: "FK_ShoppingCartPackage_ShoppingCart_ShoppingCartId", + column: x => x.ShoppingCartId, + principalTable: "ShoppingCart", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_Lead_CustomerId", + table: "Lead", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Order_CustomerId", + table: "Order", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_OrderRefund_OrderId", + table: "OrderRefund", + column: "OrderId"); + + migrationBuilder.CreateIndex( + name: "IX_ProductPrice_ProductId", + table: "ProductPrice", + column: "ProductId"); + + migrationBuilder.CreateIndex( + name: "IX_Quote_CustomerId", + table: "Quote", + column: "CustomerId"); + + migrationBuilder.CreateIndex( + name: "IX_Quote_OrderId", + table: "Quote", + column: "OrderId", + unique: true); + + 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_ShoppingCart_OrderId", + table: "ShoppingCart", + column: "OrderId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartPackage_PackageId", + table: "ShoppingCartPackage", + column: "PackageId"); + + migrationBuilder.CreateIndex( + name: "IX_ShoppingCartPackage_ShoppingCartId", + table: "ShoppingCartPackage", + column: "ShoppingCartId"); + + migrationBuilder.AddForeignKey( + name: "FK_PackageItem_ProductPrice_ProductPriceId", + table: "PackageItem", + column: "ProductPriceId", + principalTable: "ProductPrice", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ProductPrice_ProductPriceId", + table: "ShoppingCartItems", + column: "ProductPriceId", + principalTable: "ProductPrice", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + + migrationBuilder.AddForeignKey( + name: "FK_ShoppingCartItems_ShoppingCart_ShoppingCartId", + table: "ShoppingCartItems", + column: "ShoppingCartId", + principalTable: "ShoppingCart", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs b/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs index 26d9a29..26d0c26 100644 --- a/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs +++ b/LiteCharms.Features/Shop/Postgres/Migrations/ShopDbContextModelSnapshot.cs @@ -17,12 +17,81 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.7") + .HasAnnotation("ProductVersion", "10.0.8") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("LiteCharms.Entities.Customer", b => + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("ImageUrl") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("Summary") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Package", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); + + b.Property("PackageId") + .HasColumnType("uuid"); + + b.Property("ProductPriceId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PackageId"); + + b.HasIndex("ProductPriceId"); + + b.ToTable("PackageItem", (string)null); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -45,7 +114,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("Country") .HasColumnType("text"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -83,7 +152,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("Tax") .HasColumnType("text"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.Property("Website") @@ -94,10 +163,78 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasKey("Id"); - b.ToTable("Customer", (string)null); + b.ToTable("Customers", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Lead", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Models.Customer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Active") + .HasColumnType("boolean"); + + b.Property("Address") + .HasColumnType("text"); + + b.Property("City") + .HasColumnType("text"); + + b.Property("Company") + .HasColumnType("text"); + + b.Property("Country") + .HasColumnType("text"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("Discord") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LinkedIn") + .HasColumnType("text"); + + b.Property("Name") + .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") + .HasColumnType("timestamp with time zone"); + + b.Property("Website") + .HasColumnType("text"); + + b.Property("Whatsapp") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -125,7 +262,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("ClickLocation") .HasColumnType("text"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -133,6 +270,9 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("CustomerId") .HasColumnType("uuid"); + b.Property("CustomerId1") + .HasColumnType("uuid"); + b.Property("FeedItemId") .HasColumnType("bigint"); @@ -145,7 +285,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("TargetId") .HasColumnType("bigint"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.Property("WebClickId") @@ -155,10 +295,12 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasIndex("CustomerId"); - b.ToTable("Lead", (string)null); + b.HasIndex("CustomerId1"); + + b.ToTable("Leads", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Notification", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Notifications.Entities.Notification", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -171,7 +313,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("CorrelationIdType") .HasColumnType("integer"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -212,15 +354,15 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations .HasColumnType("boolean") .HasDefaultValue(false); - b.Property("Recipient") - .IsRequired() - .HasColumnType("text"); - b.Property("RecipientAddress") .IsRequired() .HasColumnType("text"); - b.Property("Sender") + b.Property("RecipientName") + .IsRequired() + .HasColumnType("text"); + + b.Property("SenderAddress") .IsRequired() .HasColumnType("text"); @@ -231,7 +373,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations .IsRequired() .HasColumnType("text"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -239,13 +381,13 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.ToTable("Notification", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Order", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -269,17 +411,17 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.PrimitiveCollection("Terms") .HasColumnType("jsonb"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); b.HasIndex("CustomerId"); - b.ToTable("Order", (string)null); + b.ToTable("Orders", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -289,7 +431,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations .HasPrecision(18, 2) .HasColumnType("numeric(18,2)"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -305,79 +447,10 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasIndex("OrderId"); - b.ToTable("OrderRefund", (string)null); + b.ToTable("OrderRefunds", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Package", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(2048) - .HasColumnType("character varying(2048)"); - - b.Property("ImageUrl") - .HasMaxLength(2048) - .HasColumnType("character varying(2048)"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("Summary") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("UpdatedAt") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("Package", (string)null); - }); - - modelBuilder.Entity("LiteCharms.Entities.PackageItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("CreatedAt") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("now()"); - - b.Property("PackageId") - .HasColumnType("uuid"); - - b.Property("ProductPriceId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("PackageId"); - - b.HasIndex("ProductPriceId"); - - b.ToTable("PackageItem", (string)null); - }); - - modelBuilder.Entity("LiteCharms.Entities.Product", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -411,10 +484,10 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasKey("Id"); - b.ToTable("Product", (string)null); + b.ToTable("Products", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -423,7 +496,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("Active") .HasColumnType("boolean"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -439,23 +512,23 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("ProductId") .HasColumnType("uuid"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); b.HasIndex("ProductId"); - b.ToTable("ProductPrice", (string)null); + b.ToTable("ProductPrices", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Quote", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -463,7 +536,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("CustomerId") .HasColumnType("uuid"); - b.Property("ExpiredAt") + b.Property("ExpiredAt") .HasColumnType("timestamp with time zone"); b.Property("InvoiceUrl") @@ -482,7 +555,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("Status") .HasColumnType("integer"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -495,16 +568,16 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasIndex("ShoppingCartId") .IsUnique(); - b.ToTable("Quote", (string)null); + b.ToTable("Quotes", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -515,7 +588,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Property("OrderId") .HasColumnType("uuid"); - b.Property("UpdatedAt") + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); @@ -525,46 +598,60 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasIndex("OrderId") .IsUnique(); - b.ToTable("ShoppingCart", (string)null); + b.ToTable("ShoppingCarts", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); + b.Property("CreatedAt") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValueSql("now()"); b.Property("ProductPriceId") .HasColumnType("uuid"); + b.Property("ProductPriceId1") + .HasColumnType("uuid"); + b.Property("Quantity") - .HasColumnType("integer"); + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(1); b.Property("ShoppingCartId") .HasColumnType("uuid"); - b.Property("UpdatedAt") + b.Property("ShoppingCartId1") + .HasColumnType("uuid"); + + b.Property("UpdatedAt") .HasColumnType("timestamp with time zone"); b.HasKey("Id"); b.HasIndex("ProductPriceId"); + b.HasIndex("ProductPriceId1"); + b.HasIndex("ShoppingCartId"); - b.ToTable("ShoppingCartItems"); + b.HasIndex("ShoppingCartId1"); + + b.ToTable("ShoppingCartItems", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("uuid"); - b.Property("CreatedAt") + b.Property("CreatedAt") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") .HasDefaultValueSql("now()"); @@ -581,49 +668,18 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.HasIndex("ShoppingCartId"); - b.ToTable("ShoppingCartPackage", (string)null); + b.ToTable("ShoppingCartPackages", (string)null); }); - modelBuilder.Entity("LiteCharms.Entities.Lead", b => + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.PackageItem", b => { - b.HasOne("LiteCharms.Entities.Customer", "Customer") - .WithMany("Leads") - .HasForeignKey("CustomerId"); - - b.Navigation("Customer"); - }); - - modelBuilder.Entity("LiteCharms.Entities.Order", b => - { - b.HasOne("LiteCharms.Entities.Customer", "Customer") - .WithMany("Orders") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Customer"); - }); - - modelBuilder.Entity("LiteCharms.Entities.OrderRefund", b => - { - b.HasOne("LiteCharms.Entities.Order", "Order") - .WithMany("Refunds") - .HasForeignKey("OrderId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Order"); - }); - - modelBuilder.Entity("LiteCharms.Entities.PackageItem", b => - { - b.HasOne("LiteCharms.Entities.Package", "Package") + b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package") .WithMany("PackageItems") .HasForeignKey("PackageId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice") .WithMany() .HasForeignKey("ProductPriceId") .OnDelete(DeleteBehavior.Restrict) @@ -634,9 +690,45 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("ProductPrice"); }); - modelBuilder.Entity("LiteCharms.Entities.ProductPrice", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Leads.Entities.Lead", b => { - b.HasOne("LiteCharms.Entities.Product", "Product") + b.HasOne("LiteCharms.Features.Shop.Customers.Models.Customer", "Customer") + .WithMany() + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict); + + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", null) + .WithMany("Leads") + .HasForeignKey("CustomerId1"); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => + { + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") + .WithMany("Orders") + .HasForeignKey("CustomerId") + .OnDelete(DeleteBehavior.Restrict) + .IsRequired(); + + b.Navigation("Customer"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.OrderRefund", b => + { + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") + .WithMany("Refunds") + .HasForeignKey("OrderId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Order"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.ProductPrice", b => + { + b.HasOne("LiteCharms.Features.Shop.Products.Entities.Product", "Product") .WithMany("ProductPrices") .HasForeignKey("ProductId") .OnDelete(DeleteBehavior.Restrict) @@ -645,21 +737,21 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("Product"); }); - modelBuilder.Entity("LiteCharms.Entities.Quote", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Quotes.Entities.Quote", b => { - b.HasOne("LiteCharms.Entities.Customer", "Customer") + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") .WithMany("Quotes") .HasForeignKey("CustomerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Order", "Order") + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") .WithOne("Quote") - .HasForeignKey("LiteCharms.Entities.Quote", "OrderId"); + .HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "OrderId"); - b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") .WithOne("Quote") - .HasForeignKey("LiteCharms.Entities.Quote", "ShoppingCartId"); + .HasForeignKey("LiteCharms.Features.Shop.Quotes.Entities.Quote", "ShoppingCartId"); b.Navigation("Customer"); @@ -668,17 +760,17 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("ShoppingCart"); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => { - b.HasOne("LiteCharms.Entities.Customer", "Customer") + b.HasOne("LiteCharms.Features.Shop.Customers.Entities.Customer", "Customer") .WithMany("ShoppingCarts") .HasForeignKey("CustomerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.HasOne("LiteCharms.Entities.Order", "Order") + b.HasOne("LiteCharms.Features.Shop.Orders.Entities.Order", "Order") .WithOne("ShoppingCart") - .HasForeignKey("LiteCharms.Entities.ShoppingCart", "OrderId") + .HasForeignKey("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "OrderId") .OnDelete(DeleteBehavior.SetNull); b.Navigation("Customer"); @@ -686,34 +778,42 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("Order"); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCartItem", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartItem", b => { - b.HasOne("LiteCharms.Entities.ProductPrice", "ProductPrice") + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", null) .WithMany() .HasForeignKey("ProductPriceId") - .OnDelete(DeleteBehavior.Cascade) + .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + b.HasOne("LiteCharms.Features.Shop.Products.Entities.ProductPrice", "ProductPrice") + .WithMany() + .HasForeignKey("ProductPriceId1"); + + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", null) .WithMany("ShoppingCartItems") .HasForeignKey("ShoppingCartId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") + .WithMany() + .HasForeignKey("ShoppingCartId1"); + b.Navigation("ProductPrice"); b.Navigation("ShoppingCart"); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCartPackage", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCartPackage", b => { - b.HasOne("LiteCharms.Entities.Package", "Package") + b.HasOne("LiteCharms.Features.Shop.CartPackages.Entities.Package", "Package") .WithMany() .HasForeignKey("PackageId") .OnDelete(DeleteBehavior.Restrict) .IsRequired(); - b.HasOne("LiteCharms.Entities.ShoppingCart", "ShoppingCart") + b.HasOne("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", "ShoppingCart") .WithMany("ShoppingCartPackages") .HasForeignKey("ShoppingCartId") .OnDelete(DeleteBehavior.Cascade) @@ -724,7 +824,12 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("ShoppingCart"); }); - modelBuilder.Entity("LiteCharms.Entities.Customer", b => + modelBuilder.Entity("LiteCharms.Features.Shop.CartPackages.Entities.Package", b => + { + b.Navigation("PackageItems"); + }); + + modelBuilder.Entity("LiteCharms.Features.Shop.Customers.Entities.Customer", b => { b.Navigation("Leads"); @@ -735,7 +840,7 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("ShoppingCarts"); }); - modelBuilder.Entity("LiteCharms.Entities.Order", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Orders.Entities.Order", b => { b.Navigation("Quote"); @@ -744,17 +849,12 @@ namespace LiteCharms.Features.Shop.Postgres.Migrations b.Navigation("ShoppingCart"); }); - modelBuilder.Entity("LiteCharms.Entities.Package", b => - { - b.Navigation("PackageItems"); - }); - - modelBuilder.Entity("LiteCharms.Entities.Product", b => + modelBuilder.Entity("LiteCharms.Features.Shop.Products.Entities.Product", b => { b.Navigation("ProductPrices"); }); - modelBuilder.Entity("LiteCharms.Entities.ShoppingCart", b => + modelBuilder.Entity("LiteCharms.Features.Shop.ShoppingCarts.Entities.ShoppingCart", b => { b.Navigation("Quote"); diff --git a/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs b/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs index b02f630..b8d7cc9 100644 --- a/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs +++ b/LiteCharms.Features/Shop/Postgres/ShopDbContext.cs @@ -36,4 +36,34 @@ public class ShopDbContext(DbContextOptions options) : DbContext( public DbSet PackageItems { get; set; } public DbSet ShoppingCartPackages { get; set; } + + //protected override void OnModelCreating(ModelBuilder modelBuilder) + //{ + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + // modelBuilder.Ignore(); + + // modelBuilder.ApplyConfiguration(new CustomerConfiguration()); + // modelBuilder.ApplyConfiguration(new LeadConfiguration()); + // modelBuilder.ApplyConfiguration(new OrderConfiguration()); + // modelBuilder.ApplyConfiguration(new ProductConfiguration()); + // modelBuilder.ApplyConfiguration(new ProductPriceConfiguration()); + // modelBuilder.ApplyConfiguration(new NotificationConfiguration()); + // modelBuilder.ApplyConfiguration(new QuoteConfiguration()); + // modelBuilder.ApplyConfiguration(new ShoppingCartConfiguration()); + // modelBuilder.ApplyConfiguration(new ShoppingCartItemConfiguration()); + // modelBuilder.ApplyConfiguration(new PackageConfirguration()); + // modelBuilder.ApplyConfiguration(new PackageItemConfiguration()); + // modelBuilder.ApplyConfiguration(new ShoppingCartPackageConfiguration()); + + // base.OnModelCreating(modelBuilder); + //} } diff --git a/LiteCharms.Features/Shop/Products/Entities/Product.cs b/LiteCharms.Features/Shop/Products/Entities/Product.cs index 5aa0b99..a48774b 100644 --- a/LiteCharms.Features/Shop/Products/Entities/Product.cs +++ b/LiteCharms.Features/Shop/Products/Entities/Product.cs @@ -3,5 +3,6 @@ [EntityTypeConfiguration] public class Product : Models.Product { + public virtual ICollection? ProductPrices { get; set; } } diff --git a/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs b/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs index ee2fb68..4307e62 100644 --- a/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs +++ b/LiteCharms.Features/Shop/Products/Entities/ProductConfiguration.cs @@ -4,7 +4,7 @@ public class ProductConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Product)); + builder.ToTable("Products"); builder.HasKey(f => f.Id); builder.Property(f => f.Name).IsRequired(); diff --git a/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs b/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs index a8daa83..beab2ca 100644 --- a/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs +++ b/LiteCharms.Features/Shop/Products/Entities/ProductPriceConfiguration.cs @@ -4,7 +4,7 @@ public class ProductPriceConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(ProductPrice)); + builder.ToTable("ProductPrices"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).ValueGeneratedOnAdd().HasDefaultValueSql("now()"); diff --git a/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs b/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs index 1f44247..fc1860c 100644 --- a/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs +++ b/LiteCharms.Features/Shop/Products/Models/ProductPrice.cs @@ -4,9 +4,9 @@ public class ProductPrice { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public Guid ProductId { get; set; } diff --git a/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs b/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs index 299044e..1363973 100644 --- a/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs +++ b/LiteCharms.Features/Shop/Quotes/Entities/QuoteConfiguration.cs @@ -4,12 +4,12 @@ public class QuoteConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(Quote)); + builder.ToTable("Quotes"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); - builder.Property(f => f.ExpiredAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); + builder.Property(f => f.ExpiredAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.CustomerId).IsRequired(); builder.Property(f => f.OrderId); builder.Property(f => f.ShoppingCartId); diff --git a/LiteCharms.Features/Shop/Quotes/Models/Quote.cs b/LiteCharms.Features/Shop/Quotes/Models/Quote.cs index 5422348..0b5ecaf 100644 --- a/LiteCharms.Features/Shop/Quotes/Models/Quote.cs +++ b/LiteCharms.Features/Shop/Quotes/Models/Quote.cs @@ -4,11 +4,11 @@ public class Quote { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } - public DateTimeOffset? ExpiredAt { get; set; } + public DateTime? ExpiredAt { get; set; } public Guid CustomerId { get; set; } diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs index e45a04b..1609973 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartConfiguration.cs @@ -4,11 +4,11 @@ public class ShoppingCartConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { - builder.ToTable(nameof(ShoppingCart)); + builder.ToTable("ShoppingCarts"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.CustomerId).IsRequired(); builder.Property(f => f.OrderId); diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs index 50afb9d..b93d7da 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItem.cs @@ -2,6 +2,7 @@ namespace LiteCharms.Features.Shop.ShoppingCarts.Entities; +[EntityTypeConfiguration] public class ShoppingCartItem : Models.ShoppingCartItem { public virtual ShoppingCart? ShoppingCart { get; set; } diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs index 3dbc5f7..7f13aca 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartItemConfiguration.cs @@ -6,11 +6,11 @@ public class ShoppingCartItemConfiguration : IEntityTypeConfiguration builder) { - builder.ToTable(nameof(ShoppingCartItem)); + builder.ToTable("ShoppingCartItems"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); - builder.Property(f => f.UpdatedAt).IsRequired(false); + builder.Property(f => f.UpdatedAt).IsRequired(false).HasDefaultValueSql(null); builder.Property(f => f.Quantity).IsRequired().HasDefaultValue(1); builder.Property(f => f.ShoppingCartId).IsRequired(); builder.Property(f => f.ProductPriceId).IsRequired(); diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs index ee08e91..d6dc310 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Entities/ShoppingCartPackageConfiguration.cs @@ -4,7 +4,7 @@ public class ShoppingCartPackageConfiguration : IEntityTypeConfiguration builder) { - builder.ToTable(nameof(ShoppingCartPackage)); + builder.ToTable("ShoppingCartPackages"); builder.HasKey(f => f.Id); builder.Property(f => f.CreatedAt).IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("now()"); diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs index fe6f633..46c52d0 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCart.cs @@ -4,9 +4,9 @@ public class ShoppingCart { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset? UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public Guid CustomerId { get; set; } diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs index 8dcc0be..99eeef6 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartItem.cs @@ -8,9 +8,9 @@ public class ShoppingCartItem public Guid ProductPriceId { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } - public DateTimeOffset UpdatedAt { get; set; } + public DateTime? UpdatedAt { get; set; } public int Quantity { get; set; } } diff --git a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs index fdc6d7b..a633be2 100644 --- a/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs +++ b/LiteCharms.Features/Shop/ShoppingCarts/Models/ShoppingCartPackage.cs @@ -4,7 +4,7 @@ public class ShoppingCartPackage { public Guid Id { get; set; } - public DateTimeOffset CreatedAt { get; set; } + public DateTime CreatedAt { get; set; } public Guid ShoppingCartId { get; set; } diff --git a/LiteCharms.Features/appsettings.json b/LiteCharms.Features/appsettings.json new file mode 100644 index 0000000..aec5c2e --- /dev/null +++ b/LiteCharms.Features/appsettings.json @@ -0,0 +1,22 @@ +{ + "Email": { + "Credentials": { + "Username": "shop@litecharms.co.za" + }, + "Port": 465, + "Host": "mail.litecharms.co.za", + "UseSsl": true + }, + "Monitoring": { + "ApiKey": "", + "Address": "http://aspire-dashboard-service.aspire.svc.cluster.local:18889", + "ServiceName": "LiteCharms.LeadGenerator" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} -- 2.47.3 From f606b8fd3c86fbdade4b56f763c023c31798b475 Mon Sep 17 00:00:00 2001 From: Khwezi Mngoma Date: Thu, 14 May 2026 02:48:46 +0200 Subject: [PATCH 5/5] Removed other packages from pipeline --- .drone.yml | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/.drone.yml b/.drone.yml index 0c10662..ededa97 100644 --- a/.drone.yml +++ b/.drone.yml @@ -16,31 +16,10 @@ steps: NEXUS_KEY: { from_secret: nexus_api_key } NEXUS_URL: https://nexus.khongisa.co.za/repository/nuget-hosted/ VERSION: 1.${DRONE_BUILD_NUMBER}.0 - commands: - # Abstractions - - dotnet pack LiteCharms.Abstractions/LiteCharms.Abstractions.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - - dotnet nuget push dist/LiteCharms.Abstractions.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - - # Models - - dotnet pack LiteCharms.Models/LiteCharms.Models.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - - dotnet nuget push dist/LiteCharms.Models.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - - # Infrastructure - - dotnet pack LiteCharms.Infrastructure/LiteCharms.Infrastructure.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - - dotnet nuget push dist/LiteCharms.Infrastructure.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - - # Features + commands: - dotnet pack LiteCharms.Features/LiteCharms.Features.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - dotnet nuget push dist/LiteCharms.Features.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - # Extensions - - dotnet pack LiteCharms.Extensions/LiteCharms.Extensions.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - - dotnet nuget push dist/LiteCharms.Extensions.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - - # Entities - - dotnet pack LiteCharms.Entities/LiteCharms.Entities.csproj -c Release -p:PackageVersion=$VERSION -o dist/ - - dotnet nuget push dist/LiteCharms.Entities.$VERSION.nupkg --api-key $NEXUS_KEY --source $NEXUS_URL - - name: gitea-tag-release image: alpine/git environment: @@ -61,7 +40,7 @@ steps: \"tag_name\": \"$VERSION\", \"target_commitish\": \"${DRONE_COMMIT_SHA}\", \"name\": \"Library Suite $VERSION\", - \"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Abstractions\n* LiteCharms.Models\n* LiteCharms.Infrastructure\n* LiteCharms.Features\n* LiteCharms.Extensions\n* LiteCharms.Entities\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\", + \"body\": \"### Published NuGet Packages\nAll packages versioned as **$VERSION**:\n* LiteCharms.Abstractions\n* LiteCharms.Features\n\n[View in Nexus](https://nexus.khongisa.co.za/repository/nuget-group/)\", \"draft\": false, \"prerelease\": false }" -- 2.47.3