From 045a652cdc65b5d8a06227c565e44b50d0da08f6 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Thu, 3 Feb 2022 19:11:33 +0300 Subject: [PATCH 1/9] feat: set up extensible organization upgrade system --- .../FeatureOfPlanDefinitionDto.cs | 19 +++ .../Organizations/IOrganizationAppService.cs | 3 + .../Organizations/PlanInfoDefinitionDto.cs | 25 +++ .../EventHubApplicationAutoMapperProfile.cs | 4 + .../Organizations/OrganizationAppService.cs | 15 +- .../Organizations/OrganizationContst.cs | 2 + ...ymentRequestExtraParameterConfiguration.cs | 6 +- .../Organizations/OrganizationPlanType.cs | 5 +- .../FeatureOfPlanDefinition.cs | 23 +++ .../IPlanInfoDefinitionStore.cs | 9 + .../PaymentRequestEventHandler.cs | 6 +- .../PaymentRequests/PlanInfoDefinition.cs | 37 ++++ .../PlanInfoDefinitionStore.cs | 21 +++ .../PaymentRequests/PlanInfoOptions.cs | 30 ++++ .../EventHubHttpApiHostModule.cs | 45 +++++ src/EventHub.HttpApi.Host/appsettings.json | 31 +++- .../Organizations/OrganizationController.cs | 8 + src/EventHub.Web/EventHubWebModule.cs | 18 -- ..._upgradeOrExtendOrganizationSection.cshtml | 17 +- .../Pages/Payment/PlanInfoHelper.cs | 73 ++++++++ .../Pages/Payment/PreCheckout.cshtml | 13 +- src/EventHub.Web/Pages/Pricing.cshtml | 161 +++++++++--------- src/EventHub.Web/Pages/Pricing.cshtml.cs | 42 +++-- .../PaymentRequests/PremiumPlanInfoOptions.cs | 85 --------- src/EventHub.Web/appsettings.json | 9 - 25 files changed, 464 insertions(+), 243 deletions(-) create mode 100644 src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs create mode 100644 src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs create mode 100644 src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs create mode 100644 src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs create mode 100644 src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs create mode 100644 src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs create mode 100644 src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs create mode 100644 src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs delete mode 100644 src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs diff --git a/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs b/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs new file mode 100644 index 0000000..d4e1e0d --- /dev/null +++ b/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace EventHub.Organizations; + +public class FeatureOfPlanDefinitionDto +{ + public uint? MaxAllowedEventsCount { get; set; } + + public uint? MaxAllowedTracksCountInOneEvent { get; set; } + + public uint? MaxAllowedAttendeesCountInOneEvent { get; set; } + + public List AdditionalFeatureInfos { get; set; } + + public FeatureOfPlanDefinitionDto() + { + AdditionalFeatureInfos = new List(); + } +} diff --git a/src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs b/src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs index ce98bbb..8a0b4ec 100644 --- a/src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs +++ b/src/EventHub.Application.Contracts/Organizations/IOrganizationAppService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Services; @@ -21,5 +22,7 @@ namespace EventHub.Organizations Task UpdateAsync(Guid id, UpdateOrganizationDto input); Task GetProfilePictureAsync(Guid id); + + Task> GetPlanInfosAsync(); } } diff --git a/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs b/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs new file mode 100644 index 0000000..9c9de31 --- /dev/null +++ b/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs @@ -0,0 +1,25 @@ +namespace EventHub.Organizations; + +public class PlanInfoDefinitionDto +{ + public OrganizationPlanType PlanType { get; set; } + + public string Description { get; set; } + + public bool IsActive { get; set; } + + public decimal Price { get; set; } + + public bool IsExtendable { get; set; } + + public int? CanBeExtendedAfterHowManyMonths { get; set; } + + public int? OnePremiumPeriodAsMonth { get; set; } + + public FeatureOfPlanDefinitionDto Feature { get; set; } + + public PlanInfoDefinitionDto() + { + Feature = new FeatureOfPlanDefinitionDto(); + } +} diff --git a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs index f16f205..b061662 100644 --- a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs +++ b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs @@ -5,6 +5,7 @@ using EventHub.Events.Registrations; using EventHub.Members; using EventHub.Organizations; using EventHub.Organizations.Memberships; +using EventHub.Organizations.PaymentRequests; using EventHub.Users; using Volo.Abp.AutoMapper; using Volo.Abp.Identity; @@ -48,6 +49,9 @@ namespace EventHub CreateMap(); CreateMap(); + + CreateMap(); + CreateMap(); } } } diff --git a/src/EventHub.Application/Organizations/OrganizationAppService.cs b/src/EventHub.Application/Organizations/OrganizationAppService.cs index 3205487..cf19673 100644 --- a/src/EventHub.Application/Organizations/OrganizationAppService.cs +++ b/src/EventHub.Application/Organizations/OrganizationAppService.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using EventHub.Organizations.Memberships; +using EventHub.Organizations.PaymentRequests; using EventHub.Users; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; @@ -21,19 +22,22 @@ namespace EventHub.Organizations private readonly OrganizationManager _organizationManager; private readonly IBlobContainer _organizationBlobContainer; private readonly IUserRepository _userRepository; + private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; public OrganizationAppService( IRepository organizationRepository, IOrganizationMembershipRepository organizationMembershipsRepository, OrganizationManager organizationManager, IBlobContainer organizationBlobContainer, - IUserRepository userRepository) + IUserRepository userRepository, + IPlanInfoDefinitionStore planInfoDefinitionStore) { _organizationRepository = organizationRepository; _organizationMembershipsRepository = organizationMembershipsRepository; _organizationManager = organizationManager; _organizationBlobContainer = organizationBlobContainer; _userRepository = userRepository; + _planInfoDefinitionStore = planInfoDefinitionStore; } [Authorize] @@ -163,6 +167,13 @@ namespace EventHub.Organizations return new RemoteStreamContent(pictureContent, blobName); } + [Authorize] + public async Task> GetPlanInfosAsync() + { + var dd = await _planInfoDefinitionStore.GetPlanInfosAsync(); + return ObjectMapper.Map, List>(dd); + } + private async Task SaveProfilePictureAsync(Guid id, IRemoteStreamContent streamContent) { var blobName = id.ToString(); @@ -170,4 +181,4 @@ namespace EventHub.Organizations await _organizationBlobContainer.SaveAsync(blobName, streamContent.GetStream(), overrideExisting: true); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs b/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs index 1097ad3..89dd99e 100644 --- a/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs +++ b/src/EventHub.Domain.Shared/Organizations/OrganizationContst.cs @@ -27,6 +27,8 @@ public const int MaxMediumUsernameLength = 24; + public const string ProductNamePrefix = "EventHub"; + public static string[] AllowedProfilePictureExtensions = { ".jpg", ".png" }; } } diff --git a/src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs b/src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs index 4954fc3..dccf4d0 100644 --- a/src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs +++ b/src/EventHub.Domain.Shared/Organizations/OrganizationPaymentRequestExtraParameterConfiguration.cs @@ -6,6 +6,8 @@ namespace EventHub.Organizations public bool IsExtend { get; set; } - public int PremiumPeriodAsMonth { get; set; } + public int? PremiumPeriodAsMonth { get; set; } + + public OrganizationPlanType TargetPlanType { get; set; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs b/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs index c48cdd7..1e21e68 100644 --- a/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs +++ b/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs @@ -2,6 +2,7 @@ namespace EventHub.Organizations { public enum OrganizationPlanType : byte { - Premium, + Free = 0, + Premium } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs b/src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs new file mode 100644 index 0000000..0c29a7f --- /dev/null +++ b/src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace EventHub.Organizations.PaymentRequests; + +public class FeatureOfPlanDefinition +{ + [CanBeNull] + [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public uint? MaxAllowedEventsCount { get; set; } + + [CanBeNull] + [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public uint? MaxAllowedTracksCountInOneEvent { get; set; } + + [CanBeNull] + [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public uint? MaxAllowedAttendeesCountInOneEvent { get; set; } + + [NotNull] + public List AdditionalFeatureInfos { get; set; } +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs new file mode 100644 index 0000000..a064388 --- /dev/null +++ b/src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace EventHub.Organizations.PaymentRequests; + +public interface IPlanInfoDefinitionStore +{ + Task> GetPlanInfosAsync(); +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs b/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs index c972580..39fe0bc 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs +++ b/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs @@ -135,14 +135,14 @@ namespace EventHub.Organizations.PaymentRequests if (organizationPaymentRequestExtraParameter.IsExtend) { organization.UpgradeToPremium( - organization.PremiumEndDate!.Value.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth)); + organization.PremiumEndDate!.Value.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); } else { - organization.UpgradeToPremium(DateTime.Now.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth)); + organization.UpgradeToPremium(DateTime.Now.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); } return await _organizationRepository.UpdateAsync(organization, true); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs new file mode 100644 index 0000000..6fad99e --- /dev/null +++ b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using JetBrains.Annotations; + +namespace EventHub.Organizations.PaymentRequests; + +public class PlanInfoDefinition +{ + public const string PlanInfo = "PlanInfos"; + + public OrganizationPlanType PlanType { get; set; } + + public string Description { get; set; } + + public bool IsActive { get; set; } + + [Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public decimal Price { get; set; } = 1.0M; + + public bool IsExtendable { get; set; } + + [CanBeNull] + [Range(0, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public int? CanBeExtendedAfterHowManyMonths { get; set; } + + [CanBeNull] + [Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + public int? OnePremiumPeriodAsMonth { get; set; } = 12; + + public FeatureOfPlanDefinition Feature { get; set; } + + public PlanInfoDefinition() + { + Feature = new FeatureOfPlanDefinition(); + Feature.AdditionalFeatureInfos = new List(); + } +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs new file mode 100644 index 0000000..118e4c3 --- /dev/null +++ b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; + +namespace EventHub.Organizations.PaymentRequests; + +public class PlanInfoDefinitionStore : IPlanInfoDefinitionStore, ITransientDependency +{ + protected PlanInfoOptions PlanInfoOptions { get; } + + public PlanInfoDefinitionStore(IOptions planInfoOptions) + { + PlanInfoOptions = planInfoOptions.Value; + } + + public Task> GetPlanInfosAsync() + { + return Task.FromResult(PlanInfoOptions.Infos); + } +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs new file mode 100644 index 0000000..5e5d48d --- /dev/null +++ b/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; + +namespace EventHub.Organizations.PaymentRequests; + +public class PlanInfoOptions +{ + public List Infos { get; } + + public PlanInfoOptions() + { + Infos = new List(); + } + + public PlanInfoOptions AddPlanInfos(List infos) + { + foreach (var info in infos) + { + AddPlanInfo(info); + } + + return this; + } + + private PlanInfoOptions AddPlanInfo(PlanInfoDefinition info) + { + Infos.AddIfNotContains(info); + + return this; + } +} diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index ce0a1ec..04478b1 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -2,9 +2,11 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Linq; using EventHub.EntityFrameworkCore; using EventHub.Events; using EventHub.Organizations; +using EventHub.Organizations.PaymentRequests; using EventHub.Utils; using EventHub.Web; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -15,6 +17,7 @@ using Microsoft.AspNetCore.Localization; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using StackExchange.Redis; using Volo.Abp; @@ -63,6 +66,7 @@ namespace EventHub ConfigureBackgroundJobs(); ConfigureAutoApiControllers(); ConfigureTiming(); + ConfigurePremiumPlanInfo(context, configuration); } private void ConfigureAutoApiControllers() @@ -190,6 +194,47 @@ namespace EventHub { Configure(options => { options.Kind = DateTimeKind.Utc; }); } + + private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration) + { + context.Services.AddOptions>() + .Bind(configuration.GetSection(PlanInfoDefinition.PlanInfo)) + .ValidateDataAnnotations() + .Validate(configOfDefinitions => + { + foreach (var config in configOfDefinitions) + { + var isValidPlanType = Enum.IsDefined(typeof(OrganizationPlanType), config.PlanType); + if (!isValidPlanType) + { + return false; + } + + var isExistSamePlan = configOfDefinitions.Count(x => + x.Price == config.Price && x.PlanType == config.PlanType); + if (isExistSamePlan > 1) + { + return false; + } + + if (config.IsActive && config.IsExtendable) + { + Check.NotNull(config.OnePremiumPeriodAsMonth, nameof(config.OnePremiumPeriodAsMonth)); + Check.NotNull(config.CanBeExtendedAfterHowManyMonths, nameof(config.CanBeExtendedAfterHowManyMonths)); + return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths; + } + } + + return true; + }, "PlanInfoDefinition is not valid!"); + + var planInfos = context.Services.GetRequiredServiceLazy>>(); + + Configure(options => + { + options.AddPlanInfos(planInfos.Value.Value); + }); + } public override void OnApplicationInitialization(ApplicationInitializationContext context) { diff --git a/src/EventHub.HttpApi.Host/appsettings.json b/src/EventHub.HttpApi.Host/appsettings.json index 22c8268..2360cb6 100644 --- a/src/EventHub.HttpApi.Host/appsettings.json +++ b/src/EventHub.HttpApi.Host/appsettings.json @@ -18,5 +18,34 @@ "Secret": "PAYPAL_SECRET", "Environment": "Sandbox" } - } + }, + "PlanInfos": [ + { + "PlanType": "Free", + "Description": "For individuals and small organizations.", + "IsActive": true, + "Price": 0, + "IsExtendable": false, + "Feature": { + "MaxAllowedEventsCount": 2, + "MaxAllowedTracksCountInOneEvent": 2, + "MaxAllowedAttendeesCountInOneEvent": 1000 + } + }, + { + "PlanType": "Premium", + "Description": "For those organize regular events.", + "IsActive": true, + "Price": 29.90, + "IsExtendable": true, + "CanBeExtendedAfterHowManyMonths": 9, + "OnePremiumPeriodAsMonth": 12, + "Feature": { + "AdditionalFeatureInfos": [ + "Be more prominent in the organization list.", + "Premium badge in the organization page." + ] + } + } + ] } diff --git a/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs b/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs index b8a60c3..6b61f88 100644 --- a/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs +++ b/src/EventHub.HttpApi/Controllers/Organizations/OrganizationController.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using EventHub.Organizations; using Microsoft.AspNetCore.Mvc; @@ -84,5 +85,12 @@ namespace EventHub.Controllers.Organizations return remoteStreamContent; } + + [HttpGet] + [Route("plan-infos")] + public async Task> GetPlanInfosAsync() + { + return await _organizationAppService.GetPlanInfosAsync(); + } } } diff --git a/src/EventHub.Web/EventHubWebModule.cs b/src/EventHub.Web/EventHubWebModule.cs index 82edceb..89650e1 100644 --- a/src/EventHub.Web/EventHubWebModule.cs +++ b/src/EventHub.Web/EventHubWebModule.cs @@ -2,7 +2,6 @@ using System; using System.IO; using EventHub.Localization; using EventHub.Web.Menus; -using EventHub.Web.PaymentRequests; using EventHub.Web.Theme; using EventHub.Web.Theme.Bundling; using EventHub.Web.Utils; @@ -86,7 +85,6 @@ namespace EventHub.Web ConfigureCookies(context); ConfigureSwaggerServices(context.Services); ConfigureRazorPageOptions(); - ConfigurePremiumPlanInfo(context, configuration); } private void ConfigureBundles() @@ -233,22 +231,6 @@ namespace EventHub.Web .PersistKeysToStackExchangeRedis(redis, "EventHub-Protection-Keys"); } - private void ConfigurePremiumPlanInfo(ServiceConfigurationContext context, IConfiguration configuration) - { - context.Services.AddOptions() - .Bind(configuration.GetSection(PremiumPlanInfoOptions.OrganizationPlanInfo)) - .ValidateDataAnnotations() - .Validate(config => - { - if (config.IsActive) - { - return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths; - } - - return true; - }, "OnePremiumPeriodAsMonth must be greater than CanBeExtendedAfterHowManyMonths."); - } - public override void OnApplicationInitialization(ApplicationInitializationContext context) { var app = context.GetApplicationBuilder(); diff --git a/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml b/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml index e21c089..4823fe2 100644 --- a/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml @@ -1,11 +1,14 @@ -@using EventHub.Web.PaymentRequests -@using Microsoft.Extensions.Options +@using EventHub.Organizations @model EventHub.Organizations.OrganizationProfileDto -@inject IOptionsSnapshot PremiumPlanInfoOptionsSnapshot +@inject IOrganizationAppService OrganizationAppService + +@{ + var premiumPlanInfo = (await OrganizationAppService.GetPlanInfosAsync()).MaxBy(x => x.Price)!; +} - @if (Model.IsPremium && PremiumPlanInfoOptionsSnapshot.Value.IsActive && PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths))) + @if (Model.IsPremium && premiumPlanInfo.IsActive && premiumPlanInfo.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(premiumPlanInfo.OnePremiumPeriodAsMonth!.Value - premiumPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) { Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString() } @@ -13,11 +16,11 @@ Edit - @if (PremiumPlanInfoOptionsSnapshot.Value.IsActive) + @if (premiumPlanInfo.IsActive) { @if (Model.IsPremium) { - @if (PremiumPlanInfoOptionsSnapshot.Value.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths))) + @if (premiumPlanInfo.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(premiumPlanInfo.OnePremiumPeriodAsMonth!.Value - premiumPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) {
@@ -38,7 +41,7 @@ { } diff --git a/src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs b/src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs new file mode 100644 index 0000000..9b1346b --- /dev/null +++ b/src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EventHub.Organizations; +using Volo.Abp; + +namespace EventHub.Web.Pages.Payment; + +public static class PlanInfoHelper +{ + public static PlanInfoDefinitionDto GetPlan(OrganizationPlanType planType, List plans) + { + return plans.Single(x => x.PlanType == planType); + } + + public static string GetProductId(PlanInfoDefinitionDto premiumPlan, bool isExtend) + { + var productIdStringBuilder = new StringBuilder(OrganizationConsts.ProductNamePrefix); + productIdStringBuilder.Append("-"); + productIdStringBuilder.Append(premiumPlan.PlanType.ToString()); + productIdStringBuilder.Append("-"); + + if (isExtend) + { + if (!premiumPlan.IsExtendable) + { + throw new BusinessException(); + } + + + productIdStringBuilder.Append("Extend"); + } + else + { + productIdStringBuilder.Append("Upgrade"); + } + + return productIdStringBuilder.ToString(); + } + + public static string GetProductName(PlanInfoDefinitionDto premiumPlan, string organizationName, bool isExtend) + { + var productNameStringBuilder = new StringBuilder(OrganizationConsts.ProductNamePrefix); + productNameStringBuilder.Append(" "); + productNameStringBuilder.Append(premiumPlan.PlanType.ToString()); + productNameStringBuilder.Append(" "); + + if (isExtend) + { + if (!premiumPlan.IsExtendable) + { + throw new BusinessException(); + } + + + productNameStringBuilder.Append("Extend"); + } + else + { + productNameStringBuilder.Append("Upgrade"); + } + + productNameStringBuilder.Append(" | "); + productNameStringBuilder.Append(organizationName); + + return productNameStringBuilder.ToString(); + } + + public static string GetRestrictionInfoByCount(uint? count) + { + return count is null ? "Unlimited" : count.ToString(); + } +} diff --git a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml index 5c74935..b4b36ad 100644 --- a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml +++ b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml @@ -1,13 +1,12 @@ @page @inherits Payment.Web.Pages.PaymentPageBase @using EventHub.Organizations -@using EventHub.Web.PaymentRequests +@using EventHub.Web.Pages.Payment @using Microsoft.AspNetCore.Http -@using Microsoft.Extensions.Options @using Newtonsoft.Json @using Payment.Web.Pages.Payment @model Payment.Web.Pages.Payment.PreCheckoutPageModel -@inject IOptionsSnapshot PremiumPlanInfoOptionsSnapshot +@inject IOrganizationAppService OrganizationAppService @{ var isExistExtraProperties = Model.PaymentRequest.ExtraProperties.TryGetValue(nameof(OrganizationPaymentRequestExtraParameterConfiguration), out var ExtraProperties); @@ -22,7 +21,7 @@ throw new BadHttpRequestException(""); } var organizationName = organizationPaymentRequestExtraParameterConfiguration.OrganizationName; - var planInfo = PremiumPlanInfoOptionsSnapshot.Value; + var planInfo = PlanInfoHelper.GetPlan(organizationPaymentRequestExtraParameterConfiguration.TargetPlanType, await OrganizationAppService.GetPlanInfosAsync()); } @section scripts { @@ -38,14 +37,14 @@

Product Detail

@OrganizationPlanType.Premium Account: @organizationName.ToUpperInvariant()

- @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend) + @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend && planInfo.IsExtendable) {

Start Date: @DateTime.Now.ToShortDateString()

-

End Date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth).ToShortDateString()

+

End Date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth!.Value).ToShortDateString()

} else { -

Your license will be extended for @planInfo.OnePremiumPeriodAsMonth months.

+

Your license will be extended for @planInfo.OnePremiumPeriodAsMonth!.Value months.

}
diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index 59278b0..b655d98 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -1,11 +1,9 @@ @page "/pricing/{organizationName}" @using EventHub.Localization -@using EventHub.Web.PaymentRequests +@using EventHub.Web.Pages.Payment @using Microsoft.AspNetCore.Mvc.Localization -@using Microsoft.Extensions.Options @model EventHub.Web.Pages.Pricing @inject IHtmlLocalizer L -@inject IOptionsSnapshot PremiumPlanInfoOptionsSnapshot @section styles { @@ -14,89 +12,82 @@

Pricing

Simple plans for everyone

-
-
-
-
-

Free Account

-

For individuals and small organizations.

-
- $ - 0 +
+ @foreach (var plan in Model.PlanInfos) + { +
+
+
+

@plan.PlanType Account

+

@L[plan.Description]

+
+ $ + @plan.Price +
+
    +
  • @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedEventsCount) events in a year.
  • +
  • Up to @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedTracksCountInOneEvent).ToLower() tracks for an event.
  • +
  • Up to @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedAttendeesCountInOneEvent).ToLower() attendees per event.
  • + @foreach (var additionalFeatureInfo in plan.Feature.AdditionalFeatureInfos) + { +
  • @additionalFeatureInfo
  • + } +
+ @if (Model.Organization.IsPremium && plan.Price > 0) + { +
+ + + Extend Now +
+ } + else if (plan.IsExtendable) + { +
+ + + Upgrade to @plan.PlanType +
+ }
-
    -
  • 2 events in a year.
  • -
  • Up to 2 tracks for an event.
  • -
  • Up to 1,000 attendees per event.
  • -
-
-
-
- @if (Model.PremiumPlanInfo is not null) - { -
-
-
-

Premium Account

-

For those organize regular events.

-
- $ - @Model.PremiumPlanInfo.Price -
-
    -
  • Unlimited events.
  • -
  • Unlimited tracks and sessions for an event.
  • -
  • Unlimited attendees.
  • -
  • Be more prominent in the organization list.
  • -
  • Premium badge in the organization page.
  • -
- @if (Model.Organization.IsPremium) - { -
- - - Extend Now -
- } - else - { -
- - - Upgrade to @Model.PremiumPlanInfo.PlanType -
- } -
-
-
- } +
+
+ }

Frequently Asked Questions

-
-
-
-
-
-
-
- @{ - var canExtendBeforeHowManyMonths = PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth - PremiumPlanInfoOptionsSnapshot.Value.CanBeExtendedAfterHowManyMonths; - } -

@(canExtendBeforeHowManyMonths == PremiumPlanInfoOptionsSnapshot.Value.OnePremiumPeriodAsMonth ? "You " : "In the last " + canExtendBeforeHowManyMonths + " months of your premium period, you") can renew your plan by going to the Organization detail page. -

-
-
-
-
-
-
-
-
-
-

Don't worry about even if your organization has run out of premium, you can upgrade back to the Premium plan at any time. -

-
-
-
-
+
+
+
+
+
+
+
+ @foreach (var plan in Model.PlanInfos.Where(x => x.Price > 0)) + { + @if (plan.IsExtendable) + { + var canExtendBeforeHowManyMonths = plan!.OnePremiumPeriodAsMonth - plan!.CanBeExtendedAfterHowManyMonths; +

For @plan.PlanType, @(canExtendBeforeHowManyMonths == plan!.OnePremiumPeriodAsMonth ? "You " : "in the last " + canExtendBeforeHowManyMonths + " months of your " + plan.PlanType.ToString().ToLower() + " period, you") can renew your plan by going to the Organization detail page.

+ } + else + { +

For @plan.PlanType, when your @plan.PlanType period is over, you can buy again your plan by going to the Organization detail page.

+ } + + } +
+
+
+
+
+
+
+
+
+

Don't worry about even if your organization has run out of premium, you can upgrade back to the Premium plan at any time. +

+
+
+
+
diff --git a/src/EventHub.Web/Pages/Pricing.cshtml.cs b/src/EventHub.Web/Pages/Pricing.cshtml.cs index dde76ac..9e2c559 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml.cs +++ b/src/EventHub.Web/Pages/Pricing.cshtml.cs @@ -1,9 +1,9 @@ +using System.Collections.Generic; using System.Threading.Tasks; using EventHub.Organizations; -using EventHub.Web.PaymentRequests; +using EventHub.Web.Pages.Payment; using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Options; using Payment.PaymentRequests; using Payment.Web.PaymentRequest; using Volo.Abp.Authorization; @@ -21,26 +21,24 @@ namespace EventHub.Web.Pages [BindProperty] public OrganizationPlanType TargetPlanToUpgrade { get; set; } - [CanBeNull] - public PremiumPlanInfoOptions PremiumPlanInfo { get; private set; } + [NotNull] + public List PlanInfos { get; private set; } public OrganizationProfileDto Organization { get; private set; } private readonly IOrganizationAppService _organizationAppService; private readonly IPaymentRequestAppService _paymentRequestAppService; private readonly IPaymentUrlBuilder _paymentUrlBuilder; - private readonly IOptionsSnapshot _premiumPlanInfoOptionsSnapshot; public Pricing( IOrganizationAppService organizationAppService, IPaymentRequestAppService paymentRequestAppService, - IPaymentUrlBuilder paymentUrlBuilder, - IOptionsSnapshot premiumPlanInfoOptionsSnapshot) + IPaymentUrlBuilder paymentUrlBuilder) { _organizationAppService = organizationAppService; _paymentRequestAppService = paymentRequestAppService; _paymentUrlBuilder = paymentUrlBuilder; - _premiumPlanInfoOptionsSnapshot = premiumPlanInfoOptionsSnapshot; + PlanInfos = new List(); } public async Task OnGetAsync() @@ -50,19 +48,16 @@ namespace EventHub.Web.Pages { throw new AbpAuthorizationException(); } - - if (_premiumPlanInfoOptionsSnapshot.Value.IsActive) - { - PremiumPlanInfo = _premiumPlanInfoOptionsSnapshot.Value; - } + PlanInfos = await _organizationAppService.GetPlanInfosAsync(); } - public async Task OnPostUpgradeAsync() + public async Task OnPostUpgradeToPremiumAsync() { var organization = await GetOrganizationProfileAsync(); + PlanInfos = await _organizationAppService.GetPlanInfosAsync(); - var plan = _premiumPlanInfoOptionsSnapshot.Value; + var plan = PlanInfoHelper.GetPlan(OrganizationPlanType.Premium, PlanInfos); if (plan is null || !plan.IsActive) { Alerts.Danger("Premium plan is currently inactive!"); @@ -74,11 +69,13 @@ namespace EventHub.Web.Pages return Redirect(_paymentUrlBuilder.BuildCheckoutUrl(paymentRequest.Id).AbsoluteUri); } - public async Task OnPostExtendAsync() + public async Task OnPostExtendToPremiumAsync() { var organization = await GetOrganizationProfileAsync(); - var plan = _premiumPlanInfoOptionsSnapshot.Value; + PlanInfos = await _organizationAppService.GetPlanInfosAsync(); + + var plan = PlanInfoHelper.GetPlan(OrganizationPlanType.Premium, PlanInfos); if (plan is null || !plan.IsActive) { Alerts.Danger("Premium plan is currently inactive!"); @@ -102,7 +99,7 @@ namespace EventHub.Web.Pages } private async Task CreatePaymentRequestAsync( - PremiumPlanInfoOptions plan, + PlanInfoDefinitionDto plan, OrganizationProfileDto organization, bool isExtend = false) { @@ -110,8 +107,8 @@ namespace EventHub.Web.Pages { CustomerId = CurrentUser.GetId().ToString(), Price = plan.Price, - ProductId = PremiumPlanInfoOptions.GetProductId(plan, isExtend), - ProductName = PremiumPlanInfoOptions.GetProductName(plan, organization.Name, isExtend), + ProductId = PlanInfoHelper.GetProductId(plan, isExtend), + ProductName = PlanInfoHelper.GetProductName(plan, organization.Name, isExtend), ExtraProperties = { { @@ -120,7 +117,8 @@ namespace EventHub.Web.Pages { OrganizationName = OrganizationName, PremiumPeriodAsMonth = plan.OnePremiumPeriodAsMonth, - IsExtend = isExtend + IsExtend = isExtend, + TargetPlanType = TargetPlanToUpgrade } } } @@ -129,4 +127,4 @@ namespace EventHub.Web.Pages return paymentRequest; } } -} \ No newline at end of file +} diff --git a/src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs b/src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs deleted file mode 100644 index 3b2cef2..0000000 --- a/src/EventHub.Web/PaymentRequests/PremiumPlanInfoOptions.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using System.Text; -using EventHub.Organizations; -using Volo.Abp; - -namespace EventHub.Web.PaymentRequests -{ - public class PremiumPlanInfoOptions - { - private const string ProductNamePrefix = "EventHub"; - - public const string OrganizationPlanInfo = "PlanInfo:Premium"; - - public OrganizationPlanType PlanType { get; private set; } = OrganizationPlanType.Premium; - - [DefaultValue(false)] - public bool IsActive { get; set; } - - [Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")] - public decimal Price { get; set; } = 1.0M; - - [DefaultValue(false)] - public bool IsExtendable { get; set; } - - [Range(0, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")] - public int CanBeExtendedAfterHowManyMonths { get; set; } - - [Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")] - public int OnePremiumPeriodAsMonth { get; set; } = 12; - - public static string GetProductId(PremiumPlanInfoOptions premiumPlan, bool isExtend) - { - var productIdStringBuilder = new StringBuilder(ProductNamePrefix); - productIdStringBuilder.Append("-"); - productIdStringBuilder.Append(premiumPlan.PlanType.ToString()); - productIdStringBuilder.Append("-"); - - if (isExtend) - { - if (!premiumPlan.IsExtendable) - { - throw new BusinessException(); - } - - - productIdStringBuilder.Append("Extend"); - } - else - { - productIdStringBuilder.Append("Upgrade"); - } - - return productIdStringBuilder.ToString(); - } - - public static string GetProductName(PremiumPlanInfoOptions premiumPlan, string organizationName, bool isExtend) - { - var productNameStringBuilder = new StringBuilder(ProductNamePrefix); - productNameStringBuilder.Append(" "); - productNameStringBuilder.Append(premiumPlan.PlanType.ToString()); - productNameStringBuilder.Append(" "); - - if (isExtend) - { - if (!premiumPlan.IsExtendable) - { - throw new BusinessException(); - } - - - productNameStringBuilder.Append("Extend"); - } - else - { - productNameStringBuilder.Append("Upgrade"); - } - - productNameStringBuilder.Append(" | "); - productNameStringBuilder.Append(organizationName); - - return productNameStringBuilder.ToString(); - } - } -} \ No newline at end of file diff --git a/src/EventHub.Web/appsettings.json b/src/EventHub.Web/appsettings.json index 9758b07..2c4a520 100644 --- a/src/EventHub.Web/appsettings.json +++ b/src/EventHub.Web/appsettings.json @@ -7,14 +7,5 @@ "RequireHttpsMetadata": "true", "ClientId": "EventHub_Web", "ClientSecret": "1q2w3e*" - }, - "PlanInfo": { - "Premium": { - "IsActive": true, - "Price": 29.90, - "IsExtendable": true, - "CanBeExtendedAfterHowManyMonths": 9, - "OnePremiumPeriodAsMonth": 12 - } } } From 1327f4c678435a7f66578bf56bc152beab08ba41 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Fri, 4 Feb 2022 18:31:45 +0300 Subject: [PATCH 2/9] feat: add feature rules to event by organization plan type --- .../Organizations/OrganizationInListDto.cs | 2 +- .../Organizations/OrganizationProfileDto.cs | 6 +- .../Organizations/PlanInfoDefinitionDto.cs | 2 +- .../EventHubApplicationAutoMapperProfile.cs | 2 +- .../Events/EventAppService.cs | 5 +- .../Organizations/OrganizationAppService.cs | 5 +- .../EventHubBackgroundServicesModule.cs | 2 +- ...rganizationPaidEnrollmentEndDateWorker.cs} | 32 +- ....cs => OrganizationPlanEndDateNotifier.cs} | 20 +- .../EventHubErrorCodes.cs | 4 +- .../Localization/EventHub/en.json | 5 +- .../Organizations/OrganizationPlanType.cs | 2 +- .../EmailTemplateDefinitionProvider.cs | 6 +- .../Emailing/EmailTemplates.cs | 4 +- ....tpl => PaidEnrollmentEndDateReminder.tpl} | 0 src/EventHub.Domain/Events/Event.cs | 2 +- src/EventHub.Domain/Events/EventManager.cs | 25 +- .../Registrations/EventRegistrationManager.cs | 11 +- .../Organizations/Organization.cs | 35 +- .../PaymentRequestEventHandler.cs | 6 +- .../FeatureOfPlanDefinition.cs | 2 +- .../IPlanInfoDefinitionStore.cs | 4 +- .../Organizations/Plans/PlanFeatureManager.cs | 61 + .../PlanInfoDefinition.cs | 14 +- .../PlanInfoDefinitionStore.cs | 12 +- .../PlanInfoOptions.cs | 2 +- ...204083359_Updated_Organization.Designer.cs | 2831 +++++++++++++++++ .../20220204083359_Updated_Organization.cs | 57 + .../EventHubDbContextModelSnapshot.cs | 12 +- .../EventHubHttpApiHostModule.cs | 5 +- src/EventHub.HttpApi.Host/appsettings.json | 2 +- .../_organizationListSection.cshtml | 7 +- ..._upgradeOrExtendOrganizationSection.cshtml | 86 +- .../PlanInfoHelper.cs | 5 +- .../Pages/Organizations/Profile.cshtml | 5 +- .../Pages/Organizations/Profile.js | 2 +- .../Pages/Payment/PreCheckout.cshtml | 5 +- src/EventHub.Web/Pages/Pricing.cshtml | 25 +- src/EventHub.Web/Pages/Pricing.cshtml.cs | 13 +- src/EventHub.Web/Pages/Pricing.css | 6 +- .../EventRegistrationManager_UnitTests.cs | 8 +- 41 files changed, 3180 insertions(+), 160 deletions(-) rename src/EventHub.BackgroundServices/Organizations/{OrganizationPremiumEndDateWorker.cs => OrganizationPaidEnrollmentEndDateWorker.cs} (69%) rename src/EventHub.BackgroundServices/Organizations/{OrganizationPremiumEndDateNotifier.cs => OrganizationPlanEndDateNotifier.cs} (66%) rename src/EventHub.Domain/Emailing/Templates/{PremiumEndDateReminder.tpl => PaidEnrollmentEndDateReminder.tpl} (100%) rename src/EventHub.Domain/Organizations/{PaymentRequests => Plans}/FeatureOfPlanDefinition.cs (93%) rename src/EventHub.Domain/Organizations/{PaymentRequests => Plans}/IPlanInfoDefinitionStore.cs (57%) create mode 100644 src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs rename src/EventHub.Domain/Organizations/{PaymentRequests => Plans}/PlanInfoDefinition.cs (67%) rename src/EventHub.Domain/Organizations/{PaymentRequests => Plans}/PlanInfoDefinitionStore.cs (52%) rename src/EventHub.Domain/Organizations/{PaymentRequests => Plans}/PlanInfoOptions.cs (91%) create mode 100644 src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.Designer.cs create mode 100644 src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.cs rename src/EventHub.Web/Pages/{Payment => Organizations}/PlanInfoHelper.cs (93%) diff --git a/src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs b/src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs index a9be3eb..fec0c97 100644 --- a/src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs +++ b/src/EventHub.Application.Contracts/Organizations/OrganizationInListDto.cs @@ -11,6 +11,6 @@ namespace EventHub.Organizations public string Description { get; set; } - public bool IsPremium { get; set; } + public OrganizationPlanType PlanType { get; set; } } } diff --git a/src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs b/src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs index 3c48003..cba756a 100644 --- a/src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs +++ b/src/EventHub.Application.Contracts/Organizations/OrganizationProfileDto.cs @@ -27,8 +27,8 @@ namespace EventHub.Organizations public string MediumUsername { get; set; } - public bool IsPremium { get; set; } - - public DateTime? PremiumEndDate { get; set; } + public OrganizationPlanType PlanType { get; set; } + + public DateTime? PaidEnrollmentEndDate { get; set; } } } diff --git a/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs b/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs index 9c9de31..644ef5a 100644 --- a/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs +++ b/src/EventHub.Application.Contracts/Organizations/PlanInfoDefinitionDto.cs @@ -14,7 +14,7 @@ public class PlanInfoDefinitionDto public int? CanBeExtendedAfterHowManyMonths { get; set; } - public int? OnePremiumPeriodAsMonth { get; set; } + public int? OnePaidEnrollmentPeriodAsMonth { get; set; } public FeatureOfPlanDefinitionDto Feature { get; set; } diff --git a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs index b061662..607397b 100644 --- a/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs +++ b/src/EventHub.Application/EventHubApplicationAutoMapperProfile.cs @@ -5,7 +5,7 @@ using EventHub.Events.Registrations; using EventHub.Members; using EventHub.Organizations; using EventHub.Organizations.Memberships; -using EventHub.Organizations.PaymentRequests; +using EventHub.Organizations.Plans; using EventHub.Users; using Volo.Abp.AutoMapper; using Volo.Abp.Identity; diff --git a/src/EventHub.Application/Events/EventAppService.cs b/src/EventHub.Application/Events/EventAppService.cs index e82aa9c..891ccc3 100644 --- a/src/EventHub.Application/Events/EventAppService.cs +++ b/src/EventHub.Application/Events/EventAppService.cs @@ -299,10 +299,7 @@ namespace EventHub.Events var @event = await _eventRepository.GetAsync(id, true); await CheckIfValidOwnerAsync(@event); - @event.AddTrack( - GuidGenerator.Create(), - input.Name - ); + await _eventManager.AddTrackAsync(@event, input.Name); await _eventRepository.UpdateAsync(@event); } diff --git a/src/EventHub.Application/Organizations/OrganizationAppService.cs b/src/EventHub.Application/Organizations/OrganizationAppService.cs index cf19673..4f9f3dd 100644 --- a/src/EventHub.Application/Organizations/OrganizationAppService.cs +++ b/src/EventHub.Application/Organizations/OrganizationAppService.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using EventHub.Organizations.Memberships; using EventHub.Organizations.PaymentRequests; +using EventHub.Organizations.Plans; using EventHub.Users; using Microsoft.AspNetCore.Authorization; using Volo.Abp.Application.Dtos; @@ -170,8 +171,8 @@ namespace EventHub.Organizations [Authorize] public async Task> GetPlanInfosAsync() { - var dd = await _planInfoDefinitionStore.GetPlanInfosAsync(); - return ObjectMapper.Map, List>(dd); + var planInfoDefinitions = await _planInfoDefinitionStore.GetPlanInfosAsync(); + return ObjectMapper.Map, List>(planInfoDefinitions); } private async Task SaveProfilePictureAsync(Guid id, IRemoteStreamContent streamContent) diff --git a/src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs b/src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs index c535ed3..e4377f2 100644 --- a/src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs +++ b/src/EventHub.BackgroundServices/EventHubBackgroundServicesModule.cs @@ -47,7 +47,7 @@ namespace EventHub context.AddBackgroundWorker(); context.AddBackgroundWorker(); - context.AddBackgroundWorker(); + context.AddBackgroundWorker(); } } } diff --git a/src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateWorker.cs b/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs similarity index 69% rename from src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateWorker.cs rename to src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs index 3feede6..c4316b5 100644 --- a/src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateWorker.cs +++ b/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs @@ -12,9 +12,9 @@ using Volo.Abp.Uow; namespace EventHub.Organizations { - public class OrganizationPremiumEndDateWorker : AsyncPeriodicBackgroundWorkerBase + public class OrganizationPaidEnrollmentEndDateWorker : AsyncPeriodicBackgroundWorkerBase { - public OrganizationPremiumEndDateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory) + public OrganizationPaidEnrollmentEndDateWorker(AbpAsyncTimer timer, IServiceScopeFactory serviceScopeFactory) : base(timer, serviceScopeFactory) { Timer.Period = (int)TimeSpan.FromDays(1).TotalMilliseconds; } @@ -22,26 +22,26 @@ namespace EventHub.Organizations [UnitOfWork(isTransactional: false)] protected override async Task DoWorkAsync(PeriodicBackgroundWorkerContext workerContext) { - var organizationPremiumEndDateNotifier = workerContext.ServiceProvider.GetRequiredService(); + var organizationPaidEnrollmentEndDateNotifier = workerContext.ServiceProvider.GetRequiredService(); var organizationRepository = workerContext.ServiceProvider.GetRequiredService>(); var asyncExecuter = workerContext.ServiceProvider.GetRequiredService(); var clock = workerContext.ServiceProvider.GetRequiredService(); - await SendPremiumReminderEmail(organizationRepository, organizationPremiumEndDateNotifier, asyncExecuter, clock); + await SendPremiumReminderEmail(organizationRepository, organizationPaidEnrollmentEndDateNotifier, asyncExecuter, clock); await UpdatePremiumExpiredOrganization(organizationRepository, asyncExecuter, clock); } private async Task SendPremiumReminderEmail( IRepository organizationRepository, - OrganizationPremiumEndDateNotifier organizationPremiumEndDateNotifier, + OrganizationPlanEndDateNotifier organizationPlanEndDateNotifier, IAsyncQueryableExecuter asyncExecuter, IClock clock) { var oneMonthLater = clock.Now.AddMonths(1); var queryable = await organizationRepository.GetQueryableAsync(); var query = queryable.Where(x => - x.IsSendPremiumReminderEmail == false && - x.PremiumEndDate <= oneMonthLater + x.IsSendPaidEnrollmentReminderEmail == false && + x.PaidEnrollmentEndDate <= oneMonthLater ); var organizations = await asyncExecuter.ToListAsync(query); @@ -50,14 +50,14 @@ namespace EventHub.Organizations { try { - if (organization.IsPremium) + if (organization.PlanType != OrganizationPlanType.Free) { - if (organization.PremiumEndDate >= clock.Now) + if (organization.PaidEnrollmentEndDate >= clock.Now) { - await organizationPremiumEndDateNotifier.NotifyAsync(organization); + await organizationPlanEndDateNotifier.NotifyAsync(organization); } - organization.IsSendPremiumReminderEmail = true; + organization.IsSendPaidEnrollmentReminderEmail = true; await organizationRepository.UpdateAsync(organization); } } @@ -72,7 +72,7 @@ namespace EventHub.Organizations { var queryable = await organizationRepository.GetQueryableAsync(); var query = queryable.Where(x => - x.PremiumEndDate >= clock.Now + x.PaidEnrollmentEndDate >= clock.Now ); var organizations = await asyncExecuter.ToListAsync(query); @@ -81,13 +81,13 @@ namespace EventHub.Organizations { try { - if (!organization.IsPremium) + if (organization.PlanType == OrganizationPlanType.Free) { continue; } - organization.DowngradeToPremium(); - organization.IsSendPremiumReminderEmail = false; + organization.SetFreeToPlanType(); + organization.IsSendPaidEnrollmentReminderEmail = false; await organizationRepository.UpdateAsync(organization); } catch (Exception ex) @@ -97,4 +97,4 @@ namespace EventHub.Organizations } } } -} \ No newline at end of file +} diff --git a/src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateNotifier.cs b/src/EventHub.BackgroundServices/Organizations/OrganizationPlanEndDateNotifier.cs similarity index 66% rename from src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateNotifier.cs rename to src/EventHub.BackgroundServices/Organizations/OrganizationPlanEndDateNotifier.cs index 5642d77..334a600 100644 --- a/src/EventHub.BackgroundServices/Organizations/OrganizationPremiumEndDateNotifier.cs +++ b/src/EventHub.BackgroundServices/Organizations/OrganizationPlanEndDateNotifier.cs @@ -10,18 +10,18 @@ using Volo.Abp.TextTemplating; namespace EventHub.Organizations { - public class OrganizationPremiumEndDateNotifier : ITransientDependency + public class OrganizationPlanEndDateNotifier : ITransientDependency { private readonly IEmailSender _emailSender; private readonly ITemplateRenderer _templateRenderer; private readonly IRepository _userRepository; - private readonly ILogger _logger; + private readonly ILogger _logger; - public OrganizationPremiumEndDateNotifier( + public OrganizationPlanEndDateNotifier( IEmailSender emailSender, ITemplateRenderer templateRenderer, IRepository userRepository, - ILogger logger) + ILogger logger) { _emailSender = emailSender; _templateRenderer = templateRenderer; @@ -31,7 +31,7 @@ namespace EventHub.Organizations public async Task NotifyAsync(Organization organization) { - if (organization is null || organization.IsSendPremiumReminderEmail || !organization.IsPremium) + if (organization is null || organization.IsSendPaidEnrollmentReminderEmail || organization.PlanType != OrganizationPlanType.Free) { return; } @@ -39,21 +39,21 @@ namespace EventHub.Organizations var user = await _userRepository.FindAsync(organization.OwnerUserId); if (user is null) { - _logger.LogError("(OrganizationPremiumEndDateNotifier) User not found!"); + _logger.LogError("(OrganizationPaidEnrollmentEndDateNotifier) User not found!"); return; } var templateModel = new { OrganizationName = organization.Name, - EndDate = organization.PremiumEndDate!.Value.ToShortDateString() + EndDate = organization.PaidEnrollmentEndDate!.Value.ToShortDateString() }; await _emailSender.SendAsync( user.Email, - "EventHub: Your Premium Organization is expiring!", - await _templateRenderer.RenderAsync(EmailTemplates.PremiumEndDateReminder, templateModel) + "EventHub: Your" + organization.PlanType + "Organization is expiring!", + await _templateRenderer.RenderAsync(EmailTemplates.PaidEnrollmentEndDateReminder, templateModel) ); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs index fb7a99c..feb2609 100644 --- a/src/EventHub.Domain.Shared/EventHubErrorCodes.cs +++ b/src/EventHub.Domain.Shared/EventHubErrorCodes.cs @@ -20,6 +20,8 @@ public const string TrackNotFound = "EventHub:TrackNotFound"; public const string SessionNotFound = "EventHub:SessionNotFound"; public const string SessionTitleAlreadyExist = "EventHub:SessionTitleAlreadyExist"; - + public const string CannotCreateNewEvent = "EventHub:CannotCreateNewEvent"; + public const string CannotAddNewTrack = "EventHub:CannotAddNewTrack"; + public const string CannotRegisterToEvent = "EventHub:CannotRegisterToEvent"; } } diff --git a/src/EventHub.Domain.Shared/Localization/EventHub/en.json b/src/EventHub.Domain.Shared/Localization/EventHub/en.json index 838a9e5..9924a15 100644 --- a/src/EventHub.Domain.Shared/Localization/EventHub/en.json +++ b/src/EventHub.Domain.Shared/Localization/EventHub/en.json @@ -158,6 +158,9 @@ "EventHub:SessionNotFound": "The session not found!", "EventHub:SessionTimeShouldBeInTheEventTime": "Session time should be in the event time!", "EventHub:SessionTimeConflictsWithAnExistingSession": "Session time conflicts with an existing session!", - "EventHub:SessionTitleAlreadyExist": "The session {Title} already exists" + "EventHub:SessionTitleAlreadyExist": "The session {Title} already exists", + "EventHub:CannotCreateNewEvent": "Cannot create a new event, you need to upgrade your organization!", + "EventHub:CannotAddNewTrack": "Cannot add a new track, you need to upgrade your organization!", + "EventHub:CannotRegisterToEvent": "The maximum number of participants allowed by the organization has been reached!" } } diff --git a/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs b/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs index 1e21e68..cd0b3fd 100644 --- a/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs +++ b/src/EventHub.Domain.Shared/Organizations/OrganizationPlanType.cs @@ -3,6 +3,6 @@ namespace EventHub.Organizations public enum OrganizationPlanType : byte { Free = 0, - Premium + Premium, } } diff --git a/src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs b/src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs index e1d9cd0..0483af3 100644 --- a/src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs +++ b/src/EventHub.Domain/Emailing/EmailTemplateDefinitionProvider.cs @@ -76,12 +76,12 @@ namespace EventHub.Emailing context.Add( new TemplateDefinition( - EmailTemplates.PremiumEndDateReminder) + EmailTemplates.PaidEnrollmentEndDateReminder) .WithVirtualFilePath( - "/Emailing/Templates/PremiumEndDateReminder.tpl", + "/Emailing/Templates/PaidEnrollmentEndDateReminder.tpl", isInlineLocalized: true ) ); } } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Emailing/EmailTemplates.cs b/src/EventHub.Domain/Emailing/EmailTemplates.cs index db859ef..992451e 100644 --- a/src/EventHub.Domain/Emailing/EmailTemplates.cs +++ b/src/EventHub.Domain/Emailing/EmailTemplates.cs @@ -16,6 +16,6 @@ namespace EventHub.Emailing public const string PaymentRequestFailed = "EventHub.Organization.PaymentRequestFailed"; - public const string PremiumEndDateReminder = "EventHub.Organization.PremiumEndDateReminder"; + public const string PaidEnrollmentEndDateReminder = "EventHub.Organization.PaidEnrollmentEndDateReminder"; } -} \ No newline at end of file +} diff --git a/src/EventHub.Domain/Emailing/Templates/PremiumEndDateReminder.tpl b/src/EventHub.Domain/Emailing/Templates/PaidEnrollmentEndDateReminder.tpl similarity index 100% rename from src/EventHub.Domain/Emailing/Templates/PremiumEndDateReminder.tpl rename to src/EventHub.Domain/Emailing/Templates/PaidEnrollmentEndDateReminder.tpl diff --git a/src/EventHub.Domain/Events/Event.cs b/src/EventHub.Domain/Events/Event.cs index 7a57616..f45a647 100644 --- a/src/EventHub.Domain/Events/Event.cs +++ b/src/EventHub.Domain/Events/Event.cs @@ -129,7 +129,7 @@ namespace EventHub.Events return this; } - public Event AddTrack(Guid trackId, string name) + internal Event AddTrack(Guid trackId, string name) { if (Tracks.Any(x => x.Name == name)) { diff --git a/src/EventHub.Domain/Events/EventManager.cs b/src/EventHub.Domain/Events/EventManager.cs index d04431d..24b18a8 100644 --- a/src/EventHub.Domain/Events/EventManager.cs +++ b/src/EventHub.Domain/Events/EventManager.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using EventHub.Countries; using EventHub.Events.Registrations; using EventHub.Organizations; +using EventHub.Organizations.Plans; using Volo.Abp; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; @@ -14,15 +15,17 @@ namespace EventHub.Events private readonly EventUrlCodeGenerator _eventUrlCodeGenerator; private readonly IRepository _eventRegistrationRepository; private readonly IRepository _countriesRepository; - + private readonly PlanFeatureManager _planFeatureManager; public EventManager( EventUrlCodeGenerator eventUrlCodeGenerator, IRepository eventRegistrationRepository, - IRepository countriesRepository) + IRepository countriesRepository, + PlanFeatureManager planFeatureManager) { _eventUrlCodeGenerator = eventUrlCodeGenerator; _eventRegistrationRepository = eventRegistrationRepository; _countriesRepository = countriesRepository; + _planFeatureManager = planFeatureManager; } public async Task CreateAsync( @@ -32,6 +35,11 @@ namespace EventHub.Events DateTime endTime, string description) { + if (!await _planFeatureManager.CanCreateNewEvent(organization.Id)) + { + throw new BusinessException(EventHubErrorCodes.CannotCreateNewEvent); + } + return new Event( GuidGenerator.Create(), organization.Id, @@ -42,6 +50,19 @@ namespace EventHub.Events description ); } + + public async Task AddTrackAsync(Event @event, string name) + { + if (!await _planFeatureManager.CanAddNewTrack(@event)) + { + throw new BusinessException(EventHubErrorCodes.CannotAddNewTrack); + } + + @event.AddTrack( + GuidGenerator.Create(), + name + ); + } public async Task SetCapacityAsync( Event @event, diff --git a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs index 4f46edb..3794fac 100644 --- a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs +++ b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using EventHub.Organizations.Plans; using Volo.Abp; using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Services; @@ -13,15 +14,18 @@ namespace EventHub.Events.Registrations private readonly IEventRegistrationRepository _eventRegistrationRepository; private readonly IGuidGenerator _guidGenerator; private readonly IClock _clock; + private readonly PlanFeatureManager _planFeatureManager; public EventRegistrationManager( IEventRegistrationRepository eventRegistrationRepository, IGuidGenerator guidGenerator, - IClock clock) + IClock clock, + PlanFeatureManager planFeatureManager) { _eventRegistrationRepository = eventRegistrationRepository; _guidGenerator = guidGenerator; _clock = clock; + _planFeatureManager = planFeatureManager; } public async Task RegisterAsync( @@ -47,6 +51,11 @@ namespace EventHub.Events.Registrations .WithData("EventTitle", @event.Title); } } + + if (!await _planFeatureManager.CanRegisterToEvent(@event)) + { + throw new BusinessException(EventHubErrorCodes.CannotRegisterToEvent); + } await _eventRegistrationRepository.InsertAsync( new EventRegistration( diff --git a/src/EventHub.Domain/Organizations/Organization.cs b/src/EventHub.Domain/Organizations/Organization.cs index ccb55a0..e38c2a2 100644 --- a/src/EventHub.Domain/Organizations/Organization.cs +++ b/src/EventHub.Domain/Organizations/Organization.cs @@ -1,6 +1,8 @@ using System; +using JetBrains.Annotations; using Volo.Abp; using Volo.Abp.Domain.Entities.Auditing; +using Volo.Abp.Validation; namespace EventHub.Organizations { @@ -28,11 +30,11 @@ namespace EventHub.Organizations public int MemberCount { get; internal set; } - public bool IsPremium { get; private set; } + public OrganizationPlanType PlanType { get; private set; } - public DateTime? PremiumEndDate { get; private set; } + public DateTime? PaidEnrollmentEndDate { get; private set; } - public bool IsSendPremiumReminderEmail { get; set; } + public bool IsSendPaidEnrollmentReminderEmail { get; set; } private Organization() { @@ -50,6 +52,7 @@ namespace EventHub.Organizations SetName(name); SetDisplayName(displayName); SetDescription(description); + SetFreeToPlanType(); } internal Organization SetName(string name) @@ -70,38 +73,36 @@ namespace EventHub.Organizations return this; } - internal Organization UpgradeToPremium(DateTime endDate) + internal Organization UpgradeToPlanType(OrganizationPlanType planType, DateTime endDate) { - SetPremiumStatus(true, endDate); + SetPlanType(planType, endDate); return this; } - public Organization DowngradeToPremium() + public Organization SetFreeToPlanType() { - if (!IsPremium) + if (PlanType == OrganizationPlanType.Free) { return this; } - SetPremiumStatus(false); + SetPlanType(OrganizationPlanType.Free, DateTime.Today); return this; } - private Organization SetPremiumStatus(bool isPremium, DateTime? endDate = null) + private Organization SetPlanType(OrganizationPlanType planType, DateTime endDate) { - if (isPremium) + if (!Enum.IsDefined(typeof(OrganizationPlanType), planType)) { - Check.NotNull(endDate, nameof(endDate)); - IsPremium = true; - PremiumEndDate = endDate; - - return this; + throw new AbpValidationException(); } - - IsPremium = false; + Check.NotNull(endDate, nameof(endDate)); + PlanType = planType; + PaidEnrollmentEndDate = endDate; + return this; } } diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs b/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs index 39fe0bc..133d033 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs +++ b/src/EventHub.Domain/Organizations/PaymentRequests/PaymentRequestEventHandler.cs @@ -134,12 +134,12 @@ namespace EventHub.Organizations.PaymentRequests if (organizationPaymentRequestExtraParameter.IsExtend) { - organization.UpgradeToPremium( - organization.PremiumEndDate!.Value.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); + organization.UpgradeToPlanType(organizationPaymentRequestExtraParameter.TargetPlanType, + organization.PaidEnrollmentEndDate!.Value.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); } else { - organization.UpgradeToPremium(DateTime.Now.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); + organization.UpgradeToPlanType(organizationPaymentRequestExtraParameter.TargetPlanType, DateTime.Now.AddMonths(organizationPaymentRequestExtraParameter.PremiumPeriodAsMonth!.Value)); } return await _organizationRepository.UpdateAsync(organization, true); diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs similarity index 93% rename from src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs rename to src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs index 0c29a7f..c9724ac 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/FeatureOfPlanDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; -namespace EventHub.Organizations.PaymentRequests; +namespace EventHub.Organizations.Plans; public class FeatureOfPlanDefinition { diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs similarity index 57% rename from src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs rename to src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs index a064388..4572650 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/IPlanInfoDefinitionStore.cs +++ b/src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; using System.Threading.Tasks; -namespace EventHub.Organizations.PaymentRequests; +namespace EventHub.Organizations.Plans; public interface IPlanInfoDefinitionStore { Task> GetPlanInfosAsync(); + + Task GetPlanInfoByTypeAsync(OrganizationPlanType type); } diff --git a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs new file mode 100644 index 0000000..e5cd049 --- /dev/null +++ b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs @@ -0,0 +1,61 @@ +using System; +using System.Threading.Tasks; +using EventHub.Events; +using EventHub.Events.Registrations; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Domain.Repositories; + +namespace EventHub.Organizations.Plans; + +public class PlanFeatureManager : ITransientDependency +{ + private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; + private readonly IRepository _organizationRepository; + private readonly IRepository _eventRepository; + private readonly IEventRegistrationRepository _eventRegistrationRepository; + + public PlanFeatureManager( + IPlanInfoDefinitionStore planInfoDefinitionStore, + IRepository organizationRepository, + IRepository eventRepository, + IEventRegistrationRepository eventRegistrationRepository) + { + _planInfoDefinitionStore = planInfoDefinitionStore; + _organizationRepository = organizationRepository; + _eventRepository = eventRepository; + _eventRegistrationRepository = eventRegistrationRepository; + } + + public async Task CanCreateNewEvent(Guid organizationId) + { + var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId); + + var totalEventCountByOrganization = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId); + totalEventCountByOrganization++; + return currentPlanOfOrganization.Feature.MaxAllowedEventsCount >= totalEventCountByOrganization; + } + + public async Task CanAddNewTrack(Event @event) + { + var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); + + var tracksCount = @event.Tracks.Count; + tracksCount++; + return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent >= tracksCount; + } + + public async Task CanRegisterToEvent(Event @event) + { + var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); + + var attendeesCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); + attendeesCount++; + return currentPlanOfOrganization.Feature.MaxAllowedAttendeesCountInOneEvent >= attendeesCount; + } + + private async Task GetCurrentPlanByOrganizationIdAsync(Guid organizationId) + { + var organization = await _organizationRepository.GetAsync(organizationId); + return await _planInfoDefinitionStore.GetPlanInfoByTypeAsync(organization.PlanType); + } +} diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs similarity index 67% rename from src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs rename to src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs index 6fad99e..eafc40f 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs @@ -1,8 +1,9 @@ +using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using JetBrains.Annotations; -namespace EventHub.Organizations.PaymentRequests; +namespace EventHub.Organizations.Plans; public class PlanInfoDefinition { @@ -25,13 +26,18 @@ public class PlanInfoDefinition [CanBeNull] [Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")] - public int? OnePremiumPeriodAsMonth { get; set; } = 12; + public int? OnePaidEnrollmentPeriodAsMonth { get; set; } = 12; public FeatureOfPlanDefinition Feature { get; set; } public PlanInfoDefinition() { - Feature = new FeatureOfPlanDefinition(); - Feature.AdditionalFeatureInfos = new List(); + Feature = new FeatureOfPlanDefinition + { + MaxAllowedEventsCount = uint.MaxValue, + MaxAllowedTracksCountInOneEvent = uint.MaxValue, + MaxAllowedAttendeesCountInOneEvent = uint.MaxValue, + AdditionalFeatureInfos = new List() + }; } } diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs similarity index 52% rename from src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs rename to src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs index 118e4c3..1f1b603 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoDefinitionStore.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs @@ -1,9 +1,10 @@ using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; -namespace EventHub.Organizations.PaymentRequests; +namespace EventHub.Organizations.Plans; public class PlanInfoDefinitionStore : IPlanInfoDefinitionStore, ITransientDependency { @@ -14,8 +15,13 @@ public class PlanInfoDefinitionStore : IPlanInfoDefinitionStore, ITransientDepen PlanInfoOptions = planInfoOptions.Value; } - public Task> GetPlanInfosAsync() + public async Task> GetPlanInfosAsync() { - return Task.FromResult(PlanInfoOptions.Infos); + return await Task.FromResult(PlanInfoOptions.Infos); + } + + public async Task GetPlanInfoByTypeAsync(OrganizationPlanType type) + { + return await Task.FromResult(PlanInfoOptions.Infos.SingleOrDefault(x => x.PlanType == type)); } } diff --git a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs similarity index 91% rename from src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs rename to src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs index 5e5d48d..771c825 100644 --- a/src/EventHub.Domain/Organizations/PaymentRequests/PlanInfoOptions.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace EventHub.Organizations.PaymentRequests; +namespace EventHub.Organizations.Plans; public class PlanInfoOptions { diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.Designer.cs b/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.Designer.cs new file mode 100644 index 0000000..d8c54e3 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.Designer.cs @@ -0,0 +1,2831 @@ +// +using System; +using EventHub.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Volo.Abp.EntityFrameworkCore; + +#nullable disable + +namespace EventHub.Migrations +{ + [DbContext(typeof(EventHubDbContext))] + [Migration("20220204083359_Updated_Organization")] + partial class Updated_Organization + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) + .HasAnnotation("ProductVersion", "6.0.1") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("EventHub.Countries.Country", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("Name"); + + b.ToTable("EhCountries", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("City") + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CountryId") + .HasColumnType("uuid"); + + b.Property("CountryName") + .HasColumnType("text"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsDraft") + .HasColumnType("boolean"); + + b.Property("IsEmailSentToMembers") + .HasColumnType("boolean"); + + b.Property("IsOnline") + .HasColumnType("boolean"); + + b.Property("IsRemindingEmailSent") + .HasColumnType("boolean"); + + b.Property("IsTimingChangeEmailSent") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(true); + + b.Property("Language") + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("OnlineLink") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("TimingChangeCount") + .HasColumnType("integer"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Url") + .IsRequired() + .HasMaxLength(69) + .HasColumnType("character varying(69)"); + + b.Property("UrlCode") + .IsRequired() + .HasMaxLength(8) + .HasColumnType("character varying(8)"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.HasIndex("IsEmailSentToMembers"); + + b.HasIndex("StartTime"); + + b.HasIndex("UrlCode"); + + b.HasIndex("IsRemindingEmailSent", "StartTime"); + + b.HasIndex("OrganizationId", "StartTime"); + + b.ToTable("EhEvents", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("EventId", "UserId"); + + b.ToTable("EhEventRegistrations", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("EndTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Language") + .IsRequired() + .HasMaxLength(16) + .HasColumnType("character varying(16)"); + + b.Property("StartTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.ToTable("EhEventSessions", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Speaker", b => + { + b.Property("SessionId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("SessionId", "UserId"); + + b.HasIndex("UserId"); + + b.ToTable("EhEventSpeakers", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("EventId") + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.HasKey("Id"); + + b.HasIndex("EventId"); + + b.ToTable("EhEventTracks", (string)null); + }); + + modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("OrganizationId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("OrganizationId", "UserId"); + + b.ToTable("EhOrganizationMemberships", (string)null); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FacebookUsername") + .HasColumnType("text"); + + b.Property("GitHubUsername") + .HasColumnType("text"); + + b.Property("InstagramUsername") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsSendPaidEnrollmentReminderEmail") + .HasColumnType("boolean"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("MediumUsername") + .HasColumnType("text"); + + b.Property("MemberCount") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(32) + .HasColumnType("character varying(32)"); + + b.Property("OwnerUserId") + .HasColumnType("uuid"); + + b.Property("PaidEnrollmentEndDate") + .HasColumnType("timestamp with time zone"); + + b.Property("PlanType") + .HasColumnType("smallint"); + + b.Property("TwitterUsername") + .HasColumnType("text"); + + b.Property("Website") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("DisplayName"); + + b.HasIndex("Name"); + + b.HasIndex("OwnerUserId"); + + b.ToTable("EhOrganizations", (string)null); + }); + + modelBuilder.Entity("Payment.PaymentRequests.PaymentRequest", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Currency") + .HasColumnType("text"); + + b.Property("CustomerId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FailReason") + .HasColumnType("text"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("Price") + .HasColumnType("numeric"); + + b.Property("ProductId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ProductName") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("State") + .HasColumnType("smallint"); + + b.HasKey("Id"); + + b.HasIndex("CustomerId"); + + b.HasIndex("State"); + + b.ToTable("PayPaymentRequests", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)") + .HasColumnName("ApplicationName"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("BrowserInfo"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientId"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("ClientIpAddress"); + + b.Property("ClientName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("ClientName"); + + b.Property("Comments") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Comments"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("CorrelationId"); + + b.Property("Exceptions") + .HasColumnType("text"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("HttpMethod") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("HttpMethod"); + + b.Property("HttpStatusCode") + .HasColumnType("integer") + .HasColumnName("HttpStatusCode"); + + b.Property("ImpersonatorTenantId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorTenantId"); + + b.Property("ImpersonatorUserId") + .HasColumnType("uuid") + .HasColumnName("ImpersonatorUserId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasColumnType("text"); + + b.Property("Url") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Url"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("UserId"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "ExecutionTime"); + + b.HasIndex("TenantId", "UserId", "ExecutionTime"); + + b.ToTable("AbpAuditLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ExecutionDuration") + .HasColumnType("integer") + .HasColumnName("ExecutionDuration"); + + b.Property("ExecutionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ExecutionTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("MethodName") + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("MethodName"); + + b.Property("Parameters") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)") + .HasColumnName("Parameters"); + + b.Property("ServiceName") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("ServiceName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "ServiceName", "MethodName", "ExecutionTime"); + + b.ToTable("AbpAuditLogActions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AuditLogId") + .HasColumnType("uuid") + .HasColumnName("AuditLogId"); + + b.Property("ChangeTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ChangeTime"); + + b.Property("ChangeType") + .HasColumnType("smallint") + .HasColumnName("ChangeType"); + + b.Property("EntityId") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityId"); + + b.Property("EntityTenantId") + .HasColumnType("uuid"); + + b.Property("EntityTypeFullName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("EntityTypeFullName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("AuditLogId"); + + b.HasIndex("TenantId", "EntityTypeFullName", "EntityId"); + + b.ToTable("AbpEntityChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("EntityChangeId") + .HasColumnType("uuid"); + + b.Property("NewValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("NewValue"); + + b.Property("OriginalValue") + .HasMaxLength(512) + .HasColumnType("character varying(512)") + .HasColumnName("OriginalValue"); + + b.Property("PropertyName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("PropertyName"); + + b.Property("PropertyTypeFullName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("PropertyTypeFullName"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("EntityChangeId"); + + b.ToTable("AbpEntityPropertyChanges", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BackgroundJobs.BackgroundJobRecord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsAbandoned") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false); + + b.Property("JobArgs") + .IsRequired() + .HasMaxLength(1048576) + .HasColumnType("character varying(1048576)"); + + b.Property("JobName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("LastTryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("NextTryTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Priority") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((byte)15); + + b.Property("TryCount") + .ValueGeneratedOnAdd() + .HasColumnType("smallint") + .HasDefaultValue((short)0); + + b.HasKey("Id"); + + b.HasIndex("IsAbandoned", "NextTryTime"); + + b.ToTable("AbpBackgroundJobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ContainerId") + .HasColumnType("uuid"); + + b.Property("Content") + .HasMaxLength(2147483647) + .HasColumnType("bytea"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("ContainerId"); + + b.HasIndex("TenantId", "ContainerId", "Name"); + + b.ToTable("AbpBlobs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name"); + + b.ToTable("AbpBlobContainers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityClaimType", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("Description") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsStatic") + .HasColumnType("boolean"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("Regex") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("RegexDescription") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ValueType") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("AbpClaimTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityLinkUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SourceTenantId") + .HasColumnType("uuid"); + + b.Property("SourceUserId") + .HasColumnType("uuid"); + + b.Property("TargetTenantId") + .HasColumnType("uuid"); + + b.Property("TargetUserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("SourceUserId", "SourceTenantId", "TargetUserId", "TargetTenantId") + .IsUnique(); + + b.ToTable("AbpLinkUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDefault") + .HasColumnType("boolean") + .HasColumnName("IsDefault"); + + b.Property("IsPublic") + .HasColumnType("boolean") + .HasColumnName("IsPublic"); + + b.Property("IsStatic") + .HasColumnType("boolean") + .HasColumnName("IsStatic"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName"); + + b.ToTable("AbpRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AbpRoleClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentitySecurityLog", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Action") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("ApplicationName") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("BrowserInfo") + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("ClientId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ClientIpAddress") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CorrelationId") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Identity") + .HasMaxLength(96) + .HasColumnType("character varying(96)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TenantName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Action"); + + b.HasIndex("TenantId", "ApplicationName"); + + b.HasIndex("TenantId", "Identity"); + + b.HasIndex("TenantId", "UserId"); + + b.ToTable("AbpSecurityLogs", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("AccessFailedCount") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("AccessFailedCount"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("Email"); + + b.Property("EmailConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("EmailConfirmed"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsActive") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("IsExternal") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsExternal"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LockoutEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("LockoutEnabled"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("Name") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Name"); + + b.Property("NormalizedEmail") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedEmail"); + + b.Property("NormalizedUserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("NormalizedUserName"); + + b.Property("PasswordHash") + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("PasswordHash"); + + b.Property("PhoneNumber") + .HasMaxLength(16) + .HasColumnType("character varying(16)") + .HasColumnName("PhoneNumber"); + + b.Property("PhoneNumberConfirmed") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("PhoneNumberConfirmed"); + + b.Property("SecurityStamp") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("SecurityStamp"); + + b.Property("Surname") + .HasMaxLength(64) + .HasColumnType("character varying(64)") + .HasColumnName("Surname"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("TwoFactorEnabled") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("TwoFactorEnabled"); + + b.Property("UserName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)") + .HasColumnName("UserName"); + + b.HasKey("Id"); + + b.HasIndex("Email"); + + b.HasIndex("NormalizedEmail"); + + b.HasIndex("NormalizedUserName"); + + b.HasIndex("UserName"); + + b.ToTable("AbpUsers", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("ClaimType") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("ClaimValue") + .HasMaxLength(1024) + .HasColumnType("character varying(1024)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AbpUserClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderDisplayName") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(196) + .HasColumnType("character varying(196)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "LoginProvider"); + + b.HasIndex("LoginProvider", "ProviderKey"); + + b.ToTable("AbpUserLogins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "UserId"); + + b.HasIndex("UserId", "OrganizationUnitId"); + + b.ToTable("AbpUserOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId", "UserId"); + + b.ToTable("AbpUserRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("uuid"); + + b.Property("LoginProvider") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AbpUserTokens", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("Code") + .IsRequired() + .HasMaxLength(95) + .HasColumnType("character varying(95)") + .HasColumnName("Code"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("DisplayName") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)") + .HasColumnName("DisplayName"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("ParentId") + .HasColumnType("uuid"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("Code"); + + b.HasIndex("ParentId"); + + b.ToTable("AbpOrganizationUnits", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.Property("OrganizationUnitId") + .HasColumnType("uuid"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("OrganizationUnitId", "RoleId"); + + b.HasIndex("RoleId", "OrganizationUnitId"); + + b.ToTable("AbpOrganizationUnitRoles", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AllowedAccessTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Type"); + + b.ToTable("IdentityServerApiResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiResourceId", "Key", "Value"); + + b.ToTable("IdentityServerApiResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiResourceId", "Scope"); + + b.ToTable("IdentityServerApiResourceScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.Property("ApiResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ApiResourceId", "Type", "Value"); + + b.ToTable("IdentityServerApiResourceSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerApiScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ApiScopeId", "Type"); + + b.ToTable("IdentityServerApiScopeClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.Property("ApiScopeId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ApiScopeId", "Key", "Value"); + + b.ToTable("IdentityServerApiScopeProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AbsoluteRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenLifetime") + .HasColumnType("integer"); + + b.Property("AccessTokenType") + .HasColumnType("integer"); + + b.Property("AllowAccessTokensViaBrowser") + .HasColumnType("boolean"); + + b.Property("AllowOfflineAccess") + .HasColumnType("boolean"); + + b.Property("AllowPlainTextPkce") + .HasColumnType("boolean"); + + b.Property("AllowRememberConsent") + .HasColumnType("boolean"); + + b.Property("AllowedIdentityTokenSigningAlgorithms") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("AlwaysIncludeUserClaimsInIdToken") + .HasColumnType("boolean"); + + b.Property("AlwaysSendClientClaims") + .HasColumnType("boolean"); + + b.Property("AuthorizationCodeLifetime") + .HasColumnType("integer"); + + b.Property("BackChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("BackChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ClientClaimsPrefix") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsentLifetime") + .HasColumnType("integer"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DeviceCodeLifetime") + .HasColumnType("integer"); + + b.Property("EnableLocalLogin") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("FrontChannelLogoutSessionRequired") + .HasColumnType("boolean"); + + b.Property("FrontChannelLogoutUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("IdentityTokenLifetime") + .HasColumnType("integer"); + + b.Property("IncludeJwtId") + .HasColumnType("boolean"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("LogoUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("PairWiseSubjectSalt") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ProtocolType") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("RefreshTokenExpiration") + .HasColumnType("integer"); + + b.Property("RefreshTokenUsage") + .HasColumnType("integer"); + + b.Property("RequireClientSecret") + .HasColumnType("boolean"); + + b.Property("RequireConsent") + .HasColumnType("boolean"); + + b.Property("RequirePkce") + .HasColumnType("boolean"); + + b.Property("RequireRequestObject") + .HasColumnType("boolean"); + + b.Property("SlidingRefreshTokenLifetime") + .HasColumnType("integer"); + + b.Property("UpdateAccessTokenClaimsOnRefresh") + .HasColumnType("boolean"); + + b.Property("UserCodeType") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("UserSsoLifetime") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("ClientId"); + + b.ToTable("IdentityServerClients", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Origin") + .HasMaxLength(150) + .HasColumnType("character varying(150)"); + + b.HasKey("ClientId", "Origin"); + + b.ToTable("IdentityServerClientCorsOrigins", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("GrantType") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.HasKey("ClientId", "GrantType"); + + b.ToTable("IdentityServerClientGrantTypes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Provider") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Provider"); + + b.ToTable("IdentityServerClientIdPRestrictions", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("PostLogoutRedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "PostLogoutRedirectUri"); + + b.ToTable("IdentityServerClientPostLogoutRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "Key", "Value"); + + b.ToTable("IdentityServerClientProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("RedirectUri") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("ClientId", "RedirectUri"); + + b.ToTable("IdentityServerClientRedirectUris", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Scope") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("ClientId", "Scope"); + + b.ToTable("IdentityServerClientScopes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.Property("ClientId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(4000) + .HasColumnType("character varying(4000)"); + + b.Property("Description") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.HasKey("ClientId", "Type", "Value"); + + b.ToTable("IdentityServerClientSecrets", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Devices.DeviceFlowCodes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("DeviceCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .IsRequired() + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("UserCode") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("Id"); + + b.HasIndex("DeviceCode") + .IsUnique(); + + b.HasIndex("Expiration"); + + b.HasIndex("UserCode"); + + b.ToTable("IdentityServerDeviceFlowCodes", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Grants.PersistedGrant", b => + { + b.Property("Key") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ClientId") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("ConsumedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Data") + .IsRequired() + .HasMaxLength(50000) + .HasColumnType("character varying(50000)"); + + b.Property("Description") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Expiration") + .HasColumnType("timestamp with time zone"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("Id") + .HasColumnType("uuid"); + + b.Property("SessionId") + .HasMaxLength(100) + .HasColumnType("character varying(100)"); + + b.Property("SubjectId") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Type") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.HasKey("Key"); + + b.HasIndex("Expiration"); + + b.HasIndex("SubjectId", "ClientId", "Type"); + + b.HasIndex("SubjectId", "SessionId", "Type"); + + b.ToTable("IdentityServerPersistedGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasMaxLength(40) + .HasColumnType("character varying(40)") + .HasColumnName("ConcurrencyStamp"); + + b.Property("CreationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("CreationTime"); + + b.Property("CreatorId") + .HasColumnType("uuid") + .HasColumnName("CreatorId"); + + b.Property("DeleterId") + .HasColumnType("uuid") + .HasColumnName("DeleterId"); + + b.Property("DeletionTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("DeletionTime"); + + b.Property("Description") + .HasMaxLength(1000) + .HasColumnType("character varying(1000)"); + + b.Property("DisplayName") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Emphasize") + .HasColumnType("boolean"); + + b.Property("Enabled") + .HasColumnType("boolean"); + + b.Property("ExtraProperties") + .HasColumnType("text") + .HasColumnName("ExtraProperties"); + + b.Property("IsDeleted") + .ValueGeneratedOnAdd() + .HasColumnType("boolean") + .HasDefaultValue(false) + .HasColumnName("IsDeleted"); + + b.Property("LastModificationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("LastModificationTime"); + + b.Property("LastModifierId") + .HasColumnType("uuid") + .HasColumnName("LastModifierId"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.Property("Required") + .HasColumnType("boolean"); + + b.Property("ShowInDiscoveryDocument") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.ToTable("IdentityServerIdentityResources", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Type") + .HasMaxLength(200) + .HasColumnType("character varying(200)"); + + b.HasKey("IdentityResourceId", "Type"); + + b.ToTable("IdentityServerIdentityResourceClaims", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.Property("IdentityResourceId") + .HasColumnType("uuid"); + + b.Property("Key") + .HasMaxLength(250) + .HasColumnType("character varying(250)"); + + b.Property("Value") + .HasMaxLength(2000) + .HasColumnType("character varying(2000)"); + + b.HasKey("IdentityResourceId", "Key", "Value"); + + b.ToTable("IdentityServerIdentityResourceProperties", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.PermissionManagement.PermissionGrant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("TenantId") + .HasColumnType("uuid") + .HasColumnName("TenantId"); + + b.HasKey("Id"); + + b.HasIndex("TenantId", "Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpPermissionGrants", (string)null); + }); + + modelBuilder.Entity("Volo.Abp.SettingManagement.Setting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(128) + .HasColumnType("character varying(128)"); + + b.Property("ProviderKey") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("ProviderName") + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Value") + .IsRequired() + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.HasKey("Id"); + + b.HasIndex("Name", "ProviderName", "ProviderKey") + .IsUnique(); + + b.ToTable("AbpSettings", (string)null); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.HasOne("EventHub.Countries.Country", null) + .WithMany() + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.NoAction); + + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Registrations.EventRegistration", b => + { + b.HasOne("EventHub.Events.Event", null) + .WithMany() + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.HasOne("EventHub.Events.Track", null) + .WithMany("Sessions") + .HasForeignKey("TrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Speaker", b => + { + b.HasOne("EventHub.Events.Session", null) + .WithMany("Speakers") + .HasForeignKey("SessionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.HasOne("EventHub.Events.Event", null) + .WithMany("Tracks") + .HasForeignKey("EventId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Memberships.OrganizationMembership", b => + { + b.HasOne("EventHub.Organizations.Organization", null) + .WithMany() + .HasForeignKey("OrganizationId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Organizations.Organization", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany() + .HasForeignKey("OwnerUserId") + .OnDelete(DeleteBehavior.NoAction) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLogAction", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("Actions") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.AuditLog", null) + .WithMany("EntityChanges") + .HasForeignKey("AuditLogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityPropertyChange", b => + { + b.HasOne("Volo.Abp.AuditLogging.EntityChange", null) + .WithMany("PropertyChanges") + .HasForeignKey("EntityChangeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.BlobStoring.Database.DatabaseBlob", b => + { + b.HasOne("Volo.Abp.BlobStoring.Database.DatabaseBlobContainer", null) + .WithMany() + .HasForeignKey("ContainerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRoleClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany("Claims") + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserClaim", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Claims") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserLogin", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Logins") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserOrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("OrganizationUnits") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserRole", b => + { + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Roles") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUserToken", b => + { + b.HasOne("Volo.Abp.Identity.IdentityUser", null) + .WithMany("Tokens") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany() + .HasForeignKey("ParentId"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnitRole", b => + { + b.HasOne("Volo.Abp.Identity.OrganizationUnit", null) + .WithMany("Roles") + .HasForeignKey("OrganizationUnitId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Volo.Abp.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("UserClaims") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Properties") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Scopes") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResourceSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiResources.ApiResource", null) + .WithMany("Secrets") + .HasForeignKey("ApiResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("UserClaims") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScopeProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.ApiScopes.ApiScope", null) + .WithMany("Properties") + .HasForeignKey("ApiScopeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Claims") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientCorsOrigin", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedCorsOrigins") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientGrantType", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedGrantTypes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientIdPRestriction", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("IdentityProviderRestrictions") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientPostLogoutRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("PostLogoutRedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("Properties") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientRedirectUri", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("RedirectUris") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientScope", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("AllowedScopes") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.ClientSecret", b => + { + b.HasOne("Volo.Abp.IdentityServer.Clients.Client", null) + .WithMany("ClientSecrets") + .HasForeignKey("ClientId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceClaim", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("UserClaims") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResourceProperty", b => + { + b.HasOne("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", null) + .WithMany("Properties") + .HasForeignKey("IdentityResourceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("EventHub.Events.Event", b => + { + b.Navigation("Tracks"); + }); + + modelBuilder.Entity("EventHub.Events.Session", b => + { + b.Navigation("Speakers"); + }); + + modelBuilder.Entity("EventHub.Events.Track", b => + { + b.Navigation("Sessions"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.AuditLog", b => + { + b.Navigation("Actions"); + + b.Navigation("EntityChanges"); + }); + + modelBuilder.Entity("Volo.Abp.AuditLogging.EntityChange", b => + { + b.Navigation("PropertyChanges"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityRole", b => + { + b.Navigation("Claims"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.IdentityUser", b => + { + b.Navigation("Claims"); + + b.Navigation("Logins"); + + b.Navigation("OrganizationUnits"); + + b.Navigation("Roles"); + + b.Navigation("Tokens"); + }); + + modelBuilder.Entity("Volo.Abp.Identity.OrganizationUnit", b => + { + b.Navigation("Roles"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiResources.ApiResource", b => + { + b.Navigation("Properties"); + + b.Navigation("Scopes"); + + b.Navigation("Secrets"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.ApiScopes.ApiScope", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.Clients.Client", b => + { + b.Navigation("AllowedCorsOrigins"); + + b.Navigation("AllowedGrantTypes"); + + b.Navigation("AllowedScopes"); + + b.Navigation("Claims"); + + b.Navigation("ClientSecrets"); + + b.Navigation("IdentityProviderRestrictions"); + + b.Navigation("PostLogoutRedirectUris"); + + b.Navigation("Properties"); + + b.Navigation("RedirectUris"); + }); + + modelBuilder.Entity("Volo.Abp.IdentityServer.IdentityResources.IdentityResource", b => + { + b.Navigation("Properties"); + + b.Navigation("UserClaims"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.cs b/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.cs new file mode 100644 index 0000000..3ab72a0 --- /dev/null +++ b/src/EventHub.EntityFrameworkCore/Migrations/20220204083359_Updated_Organization.cs @@ -0,0 +1,57 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace EventHub.Migrations +{ + public partial class Updated_Organization : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "IsPremium", + table: "EhOrganizations"); + + migrationBuilder.RenameColumn( + name: "PremiumEndDate", + table: "EhOrganizations", + newName: "PaidEnrollmentEndDate"); + + migrationBuilder.RenameColumn( + name: "IsSendPremiumReminderEmail", + table: "EhOrganizations", + newName: "IsSendPaidEnrollmentReminderEmail"); + + migrationBuilder.AddColumn( + name: "PlanType", + table: "EhOrganizations", + type: "smallint", + nullable: false, + defaultValue: (byte)0); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "PlanType", + table: "EhOrganizations"); + + migrationBuilder.RenameColumn( + name: "PaidEnrollmentEndDate", + table: "EhOrganizations", + newName: "PremiumEndDate"); + + migrationBuilder.RenameColumn( + name: "IsSendPaidEnrollmentReminderEmail", + table: "EhOrganizations", + newName: "IsSendPremiumReminderEmail"); + + migrationBuilder.AddColumn( + name: "IsPremium", + table: "EhOrganizations", + type: "boolean", + nullable: false, + defaultValue: false); + } + } +} diff --git a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs index 0f35f77..cc8792a 100644 --- a/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs +++ b/src/EventHub.EntityFrameworkCore/Migrations/EventHubDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace EventHub.Migrations #pragma warning disable 612, 618 modelBuilder .HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.PostgreSql) - .HasAnnotation("ProductVersion", "6.0.0") + .HasAnnotation("ProductVersion", "6.0.1") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -376,10 +376,7 @@ namespace EventHub.Migrations .HasDefaultValue(false) .HasColumnName("IsDeleted"); - b.Property("IsPremium") - .HasColumnType("boolean"); - - b.Property("IsSendPremiumReminderEmail") + b.Property("IsSendPaidEnrollmentReminderEmail") .HasColumnType("boolean"); b.Property("LastModificationTime") @@ -404,9 +401,12 @@ namespace EventHub.Migrations b.Property("OwnerUserId") .HasColumnType("uuid"); - b.Property("PremiumEndDate") + b.Property("PaidEnrollmentEndDate") .HasColumnType("timestamp with time zone"); + b.Property("PlanType") + .HasColumnType("smallint"); + b.Property("TwitterUsername") .HasColumnType("text"); diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index 04478b1..f1ed65e 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -7,6 +7,7 @@ using EventHub.EntityFrameworkCore; using EventHub.Events; using EventHub.Organizations; using EventHub.Organizations.PaymentRequests; +using EventHub.Organizations.Plans; using EventHub.Utils; using EventHub.Web; using Microsoft.AspNetCore.Authentication.JwtBearer; @@ -219,9 +220,9 @@ namespace EventHub if (config.IsActive && config.IsExtendable) { - Check.NotNull(config.OnePremiumPeriodAsMonth, nameof(config.OnePremiumPeriodAsMonth)); + Check.NotNull(config.OnePaidEnrollmentPeriodAsMonth, nameof(config.OnePaidEnrollmentPeriodAsMonth)); Check.NotNull(config.CanBeExtendedAfterHowManyMonths, nameof(config.CanBeExtendedAfterHowManyMonths)); - return config.OnePremiumPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths; + return config.OnePaidEnrollmentPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths; } } diff --git a/src/EventHub.HttpApi.Host/appsettings.json b/src/EventHub.HttpApi.Host/appsettings.json index 2360cb6..6de340b 100644 --- a/src/EventHub.HttpApi.Host/appsettings.json +++ b/src/EventHub.HttpApi.Host/appsettings.json @@ -39,7 +39,7 @@ "Price": 29.90, "IsExtendable": true, "CanBeExtendedAfterHowManyMonths": 9, - "OnePremiumPeriodAsMonth": 12, + "OnePaidEnrollmentPeriodAsMonth": 12, "Feature": { "AdditionalFeatureInfos": [ "Be more prominent in the organization list.", diff --git a/src/EventHub.Web/Pages/Organizations/Components/OrganizationsArea/_organizationListSection.cshtml b/src/EventHub.Web/Pages/Organizations/Components/OrganizationsArea/_organizationListSection.cshtml index 858bfa1..942abd3 100644 --- a/src/EventHub.Web/Pages/Organizations/Components/OrganizationsArea/_organizationListSection.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Components/OrganizationsArea/_organizationListSection.cshtml @@ -1,3 +1,4 @@ +@using EventHub.Organizations @using EventHub.Web @using Microsoft.Extensions.Options @model List @@ -14,9 +15,9 @@
@organization.DisplayName - @if (organization.IsPremium) + @if (organization.PlanType != OrganizationPlanType.Free) { - Premium + @organization.PlanType }
@@ -29,4 +30,4 @@
-} \ No newline at end of file +} diff --git a/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml b/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml index 4823fe2..bdb2f24 100644 --- a/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Components/_upgradeOrExtendOrganizationSection.cshtml @@ -3,57 +3,65 @@ @inject IOrganizationAppService OrganizationAppService @{ - var premiumPlanInfo = (await OrganizationAppService.GetPlanInfosAsync()).MaxBy(x => x.Price)!; + PlanInfoDefinitionDto currentPlanInfo = null; + if (Model is not null) + { + var planInfos = await OrganizationAppService.GetPlanInfosAsync(); + currentPlanInfo = planInfos.SingleOrDefault(x => x.PlanType == Model.PlanType); + } } - - - @if (Model.IsPremium && premiumPlanInfo.IsActive && premiumPlanInfo.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(premiumPlanInfo.OnePremiumPeriodAsMonth!.Value - premiumPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) - { - Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString() - } - - Edit - - - @if (premiumPlanInfo.IsActive) +@if (Model is not null && currentPlanInfo is not null) +{ + + + @if (Model.PlanType != OrganizationPlanType.Free && currentPlanInfo.IsActive && currentPlanInfo.IsExtendable && Model.PaidEnrollmentEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(currentPlanInfo.OnePaidEnrollmentPeriodAsMonth!.Value - currentPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) { - @if (Model.IsPremium) + @Model.PlanType end date: @Model.PaidEnrollmentEndDate!.Value.ToShortDateString() + } + + Edit + + + @if (currentPlanInfo.IsActive) { - @if (premiumPlanInfo.IsExtendable && Model.PremiumEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(premiumPlanInfo.OnePremiumPeriodAsMonth!.Value - premiumPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) + @if (Model.PlanType != OrganizationPlanType.Free) { - + @if (currentPlanInfo.IsExtendable && Model.PaidEnrollmentEndDate!.Value.IsBetween(DateTime.Now, DateTime.Now.AddMonths(currentPlanInfo.OnePaidEnrollmentPeriodAsMonth!.Value - currentPlanInfo.CanBeExtendedAfterHowManyMonths!.Value))) + { + + } + else + { +
+ +
+ } } else {
- + + Upgrade your organization +
} } else { - + + + @Model.PlanType end date: @Model.PaidEnrollmentEndDate!.Value.ToShortDateString() + + } - } - else - { - - - Premium end date: @Model.PremiumEndDate!.Value.ToShortDateString() - - - } -
-
-
+
+
+
+} diff --git a/src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs b/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs similarity index 93% rename from src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs rename to src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs index 9b1346b..6e6d1e3 100644 --- a/src/EventHub.Web/Pages/Payment/PlanInfoHelper.cs +++ b/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs @@ -4,7 +4,7 @@ using System.Text; using EventHub.Organizations; using Volo.Abp; -namespace EventHub.Web.Pages.Payment; +namespace EventHub.Web.Pages.Organizations; public static class PlanInfoHelper { @@ -68,6 +68,7 @@ public static class PlanInfoHelper public static string GetRestrictionInfoByCount(uint? count) { - return count is null ? "Unlimited" : count.ToString(); + count ??= uint.MaxValue; + return count == uint.MaxValue ? "Unlimited" : count.ToString(); } } diff --git a/src/EventHub.Web/Pages/Organizations/Profile.cshtml b/src/EventHub.Web/Pages/Organizations/Profile.cshtml index 983c47c..42b7466 100644 --- a/src/EventHub.Web/Pages/Organizations/Profile.cshtml +++ b/src/EventHub.Web/Pages/Organizations/Profile.cshtml @@ -1,6 +1,7 @@ @page "/organizations/{name}" @inject IHtmlLocalizer L @using EventHub.Localization +@using EventHub.Organizations @using EventHub.Web @using EventHub.Web.Pages.Events.Components.EventsArea @using EventHub.Web.Pages.Organizations.Components.JoinArea @@ -47,9 +48,9 @@
@Model.Organization.DisplayName - @if (Model.Organization.IsPremium) + @if (Model.Organization.PlanType != OrganizationPlanType.Free) { - Premium + @Model.Organization.PlanType }

diff --git a/src/EventHub.Web/Pages/Organizations/Profile.js b/src/EventHub.Web/Pages/Organizations/Profile.js index 3ca78d0..857603d 100644 --- a/src/EventHub.Web/Pages/Organizations/Profile.js +++ b/src/EventHub.Web/Pages/Organizations/Profile.js @@ -20,7 +20,7 @@ $(function () { if (r.extraProperties.Status === "COMPLETED") { Swal.fire({ title: 'Thank you for your purchase!', - text: 'Your Organization has been upgraded to Premium.', + text: 'Your Organization has been upgraded.', imageUrl: '/assets/success.svg', imageHeight: "70", imageWidth: "70", diff --git a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml index b4b36ad..8143c8b 100644 --- a/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml +++ b/src/EventHub.Web/Pages/Payment/PreCheckout.cshtml @@ -1,6 +1,7 @@ @page @inherits Payment.Web.Pages.PaymentPageBase @using EventHub.Organizations +@using EventHub.Web.Pages.Organizations @using EventHub.Web.Pages.Payment @using Microsoft.AspNetCore.Http @using Newtonsoft.Json @@ -40,11 +41,11 @@ @if (!organizationPaymentRequestExtraParameterConfiguration.IsExtend && planInfo.IsExtendable) {

Start Date: @DateTime.Now.ToShortDateString()

-

End Date: @DateTime.Now.AddMonths(planInfo.OnePremiumPeriodAsMonth!.Value).ToShortDateString()

+

End Date: @DateTime.Now.AddMonths(planInfo.OnePaidEnrollmentPeriodAsMonth!.Value).ToShortDateString()

} else { -

Your license will be extended for @planInfo.OnePremiumPeriodAsMonth!.Value months.

+

Your license will be extended for @planInfo.OnePaidEnrollmentPeriodAsMonth!.Value months.

}
diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index b655d98..ba7ccf2 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -1,6 +1,6 @@ @page "/pricing/{organizationName}" @using EventHub.Localization -@using EventHub.Web.Pages.Payment +@using EventHub.Web.Pages.Organizations @using Microsoft.AspNetCore.Mvc.Localization @model EventHub.Web.Pages.Pricing @inject IHtmlLocalizer L @@ -16,7 +16,7 @@ @foreach (var plan in Model.PlanInfos) {
-
+

@plan.PlanType Account

@L[plan.Description]

@@ -33,9 +33,9 @@
  • @additionalFeatureInfo
  • } - @if (Model.Organization.IsPremium && plan.Price > 0) + @if (Model.Organization.PlanType == plan.PlanType && plan.Price > 0) { -
    + Extend Now @@ -43,10 +43,10 @@ } else if (plan.IsExtendable) { - + - Upgrade to @plan.PlanType + Upgrade to @plan.PlanType
    }
    @@ -65,8 +65,15 @@ { @if (plan.IsExtendable) { - var canExtendBeforeHowManyMonths = plan!.OnePremiumPeriodAsMonth - plan!.CanBeExtendedAfterHowManyMonths; -

    For @plan.PlanType, @(canExtendBeforeHowManyMonths == plan!.OnePremiumPeriodAsMonth ? "You " : "in the last " + canExtendBeforeHowManyMonths + " months of your " + plan.PlanType.ToString().ToLower() + " period, you") can renew your plan by going to the Organization detail page.

    + var canExtendBeforeHowManyMonths = plan.OnePaidEnrollmentPeriodAsMonth!.Value - plan.CanBeExtendedAfterHowManyMonths!.Value; + @if (canExtendBeforeHowManyMonths != 0) + { +

    For @plan.PlanType, @(canExtendBeforeHowManyMonths == plan.OnePaidEnrollmentPeriodAsMonth!.Value ? "You " : "in the last " + canExtendBeforeHowManyMonths + " months of your " + plan.PlanType.ToString().ToLower() + " period, you") can renew your plan by going to the organization detail page.

    + } + else + { +

    For @plan.PlanType, when your @plan.PlanType period expires, you can repurchase your plan by going to the organization detail page.

    + } } else { @@ -83,7 +90,7 @@
    -

    Don't worry about even if your organization has run out of premium, you can upgrade back to the Premium plan at any time. +

    Don't worry about even if your organization has run out of premium, you can upgrade back to the plan at any time.

    diff --git a/src/EventHub.Web/Pages/Pricing.cshtml.cs b/src/EventHub.Web/Pages/Pricing.cshtml.cs index 9e2c559..056d07a 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml.cs +++ b/src/EventHub.Web/Pages/Pricing.cshtml.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using EventHub.Organizations; -using EventHub.Web.Pages.Payment; +using EventHub.Web.Pages.Organizations; using JetBrains.Annotations; using Microsoft.AspNetCore.Mvc; using Payment.PaymentRequests; @@ -52,12 +52,11 @@ namespace EventHub.Web.Pages PlanInfos = await _organizationAppService.GetPlanInfosAsync(); } - public async Task OnPostUpgradeToPremiumAsync() + public async Task OnPostUpgradeAsync() { var organization = await GetOrganizationProfileAsync(); PlanInfos = await _organizationAppService.GetPlanInfosAsync(); - - var plan = PlanInfoHelper.GetPlan(OrganizationPlanType.Premium, PlanInfos); + var plan = PlanInfoHelper.GetPlan(TargetPlanToUpgrade, PlanInfos); if (plan is null || !plan.IsActive) { Alerts.Danger("Premium plan is currently inactive!"); @@ -69,13 +68,13 @@ namespace EventHub.Web.Pages return Redirect(_paymentUrlBuilder.BuildCheckoutUrl(paymentRequest.Id).AbsoluteUri); } - public async Task OnPostExtendToPremiumAsync() + public async Task OnPostExtendAsync() { var organization = await GetOrganizationProfileAsync(); PlanInfos = await _organizationAppService.GetPlanInfosAsync(); - var plan = PlanInfoHelper.GetPlan(OrganizationPlanType.Premium, PlanInfos); + var plan = PlanInfoHelper.GetPlan(TargetPlanToUpgrade, PlanInfos); if (plan is null || !plan.IsActive) { Alerts.Danger("Premium plan is currently inactive!"); @@ -116,7 +115,7 @@ namespace EventHub.Web.Pages new OrganizationPaymentRequestExtraParameterConfiguration { OrganizationName = OrganizationName, - PremiumPeriodAsMonth = plan.OnePremiumPeriodAsMonth, + PremiumPeriodAsMonth = plan.OnePaidEnrollmentPeriodAsMonth, IsExtend = isExtend, TargetPlanType = TargetPlanToUpgrade } diff --git a/src/EventHub.Web/Pages/Pricing.css b/src/EventHub.Web/Pages/Pricing.css index c92dad1..687bb2a 100644 --- a/src/EventHub.Web/Pages/Pricing.css +++ b/src/EventHub.Web/Pages/Pricing.css @@ -36,6 +36,10 @@ overflow: hidden; } +.plan-card { + height: 570px; +} + html:not([dir=rtl]) .mb-4, html:not([dir=rtl]) .my-4 { margin-bottom: 1.5rem !important; -} \ No newline at end of file +} diff --git a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs index 2b3a2d9..786282c 100644 --- a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs +++ b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs @@ -18,10 +18,10 @@ namespace EventHub.Events.Registrations clock.Now.Returns(DateTime.Now); var registrationManager = new EventRegistrationManager( - null, null, clock + null, null, clock, null ); - var evnt = new Event( + var @event = new Event( Guid.NewGuid(), Guid.NewGuid(), "1a8j3v0d", @@ -31,7 +31,7 @@ namespace EventHub.Events.Registrations "In this event, we will introduce the ABP Framework..." ); - registrationManager.IsPastEvent(evnt).ShouldBeTrue(); + registrationManager.IsPastEvent(@event).ShouldBeTrue(); } [Fact] @@ -58,7 +58,7 @@ namespace EventHub.Events.Registrations clock.Now.Returns(DateTime.Now); var registrationManager = new EventRegistrationManager( - repository, guidGenerator, clock + repository, guidGenerator, clock, null ); await registrationManager.RegisterAsync(evnt, user); From 680a754624874dd4d437464dfc7f01300009044f Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Fri, 4 Feb 2022 18:49:40 +0300 Subject: [PATCH 3/9] move validation of PlanInfoDefinition in EventHubHttpApiHostModule to PlanInfoDefinition class --- .../Organizations/Plans/PlanInfoDefinition.cs | 29 +++++++++++++++++++ .../EventHubHttpApiHostModule.cs | 29 +------------------ .../Pages/Organizations/PlanInfoHelper.cs | 2 +- src/EventHub.Web/Pages/Pricing.cshtml | 6 ++-- src/EventHub.Web/Pages/Pricing.cshtml.cs | 2 +- 5 files changed, 35 insertions(+), 33 deletions(-) diff --git a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs index eafc40f..459ceca 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; +using System.Linq; using JetBrains.Annotations; +using Volo.Abp; namespace EventHub.Organizations.Plans; @@ -40,4 +42,31 @@ public class PlanInfoDefinition AdditionalFeatureInfos = new List() }; } + + public static bool IsValid(List definitions) + { + foreach (var definition in definitions) + { + var isValidPlanType = Enum.IsDefined(typeof(OrganizationPlanType), definition.PlanType); + if (!isValidPlanType) + { + return false; + } + + var isExistSamePlan = definitions.Count(x => x.Price == definition.Price && x.PlanType == definition.PlanType); + if (isExistSamePlan > 1) + { + return false; + } + + if (definition.IsActive && definition.IsExtendable) + { + Check.NotNull(definition.OnePaidEnrollmentPeriodAsMonth, nameof(definition.OnePaidEnrollmentPeriodAsMonth)); + Check.NotNull(definition.CanBeExtendedAfterHowManyMonths, nameof(definition.CanBeExtendedAfterHowManyMonths)); + return definition.OnePaidEnrollmentPeriodAsMonth > definition.CanBeExtendedAfterHowManyMonths; + } + } + + return true; + } } diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index f1ed65e..8c167cd 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -6,7 +6,6 @@ using System.Linq; using EventHub.EntityFrameworkCore; using EventHub.Events; using EventHub.Organizations; -using EventHub.Organizations.PaymentRequests; using EventHub.Organizations.Plans; using EventHub.Utils; using EventHub.Web; @@ -201,33 +200,7 @@ namespace EventHub context.Services.AddOptions>() .Bind(configuration.GetSection(PlanInfoDefinition.PlanInfo)) .ValidateDataAnnotations() - .Validate(configOfDefinitions => - { - foreach (var config in configOfDefinitions) - { - var isValidPlanType = Enum.IsDefined(typeof(OrganizationPlanType), config.PlanType); - if (!isValidPlanType) - { - return false; - } - - var isExistSamePlan = configOfDefinitions.Count(x => - x.Price == config.Price && x.PlanType == config.PlanType); - if (isExistSamePlan > 1) - { - return false; - } - - if (config.IsActive && config.IsExtendable) - { - Check.NotNull(config.OnePaidEnrollmentPeriodAsMonth, nameof(config.OnePaidEnrollmentPeriodAsMonth)); - Check.NotNull(config.CanBeExtendedAfterHowManyMonths, nameof(config.CanBeExtendedAfterHowManyMonths)); - return config.OnePaidEnrollmentPeriodAsMonth > config.CanBeExtendedAfterHowManyMonths; - } - } - - return true; - }, "PlanInfoDefinition is not valid!"); + .Validate(PlanInfoDefinition.IsValid, "PlanInfoDefinition is not valid!"); var planInfos = context.Services.GetRequiredServiceLazy>>(); diff --git a/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs b/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs index 6e6d1e3..43623da 100644 --- a/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs +++ b/src/EventHub.Web/Pages/Organizations/PlanInfoHelper.cs @@ -66,7 +66,7 @@ public static class PlanInfoHelper return productNameStringBuilder.ToString(); } - public static string GetRestrictionInfoByCount(uint? count) + public static string GetFeatureInfoByCount(uint? count) { count ??= uint.MaxValue; return count == uint.MaxValue ? "Unlimited" : count.ToString(); diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index ba7ccf2..687b433 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -25,9 +25,9 @@ @plan.Price
      -
    • @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedEventsCount) events in a year.
    • -
    • Up to @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedTracksCountInOneEvent).ToLower() tracks for an event.
    • -
    • Up to @PlanInfoHelper.GetRestrictionInfoByCount(plan.Feature.MaxAllowedAttendeesCountInOneEvent).ToLower() attendees per event.
    • +
    • @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedEventsCount) events in a year.
    • +
    • Up to @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedTracksCountInOneEvent).ToLower() tracks for an event.
    • +
    • Up to @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedAttendeesCountInOneEvent).ToLower() attendees per event.
    • @foreach (var additionalFeatureInfo in plan.Feature.AdditionalFeatureInfos) {
    • @additionalFeatureInfo
    • diff --git a/src/EventHub.Web/Pages/Pricing.cshtml.cs b/src/EventHub.Web/Pages/Pricing.cshtml.cs index 056d07a..0c2cc74 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml.cs +++ b/src/EventHub.Web/Pages/Pricing.cshtml.cs @@ -21,7 +21,7 @@ namespace EventHub.Web.Pages [BindProperty] public OrganizationPlanType TargetPlanToUpgrade { get; set; } - [NotNull] + [ItemNotNull] public List PlanInfos { get; private set; } public OrganizationProfileDto Organization { get; private set; } From 68ef25bb46675af52a7a7289ce78a8b963652a8b Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Tue, 8 Feb 2022 16:23:33 +0300 Subject: [PATCH 4/9] Fix tests that don't work --- .../Data/EventHubSampleDataSeedContributor.cs | 5 ++--- .../EventHubDbMigratorModule.cs | 16 ++++++++++++++++ .../Registrations/EventRegistrationManager.cs | 2 +- .../Organizations/Plans/PlanFeatureManager.cs | 6 +++--- .../EventHubHttpApiHostModule.cs | 1 - .../EventRegistrationManager_UnitTests.cs | 14 +++++++++----- test/EventHub.TestBase/EventHubTestBaseModule.cs | 14 ++++++++++++++ 7 files changed, 45 insertions(+), 13 deletions(-) diff --git a/src/EventHub.DbMigrator/Data/EventHubSampleDataSeedContributor.cs b/src/EventHub.DbMigrator/Data/EventHubSampleDataSeedContributor.cs index 29e6731..3c767a8 100644 --- a/src/EventHub.DbMigrator/Data/EventHubSampleDataSeedContributor.cs +++ b/src/EventHub.DbMigrator/Data/EventHubSampleDataSeedContributor.cs @@ -73,8 +73,7 @@ namespace EventHub.DbMigrator.Data new IdentityUser(_userSandraId = _guidGenerator.Create(), "SandraWolf", "sandra_wolf@gmail.com"), new IdentityUser(_userSergeyId = _guidGenerator.Create(), "SergeyPolgul", "sergey_polgul@gmail.com"), new IdentityUser(_userWellyId = _guidGenerator.Create(), "WellyTambunan", "welly_tambunan@gmail.com"), - new IdentityUser(_userAlessandroId = _guidGenerator.Create(), "AlessandroMuci", - "alessandro_muci@gmail.com"), + new IdentityUser(_userAlessandroId = _guidGenerator.Create(), "AlessandroMuci", "alessandro_muci@gmail.com"), new IdentityUser(_userMarkId = _guidGenerator.Create(), "MarkGodfrey", "mark_godfrey@gmail.com"), new IdentityUser(_userTonyId = _guidGenerator.Create(), "TonyBurton", "tony_burton@gmail.com"), new IdentityUser(_guidGenerator.Create(), "DavidGraus", "david_graus@gmail.com"), @@ -385,4 +384,4 @@ namespace EventHub.DbMigrator.Data } } } -#endif \ No newline at end of file +#endif diff --git a/src/EventHub.DbMigrator/EventHubDbMigratorModule.cs b/src/EventHub.DbMigrator/EventHubDbMigratorModule.cs index 40f47fb..385a78c 100644 --- a/src/EventHub.DbMigrator/EventHubDbMigratorModule.cs +++ b/src/EventHub.DbMigrator/EventHubDbMigratorModule.cs @@ -1,5 +1,7 @@ using EventHub.Admin; using EventHub.EntityFrameworkCore; +using EventHub.Organizations; +using EventHub.Organizations.Plans; using Volo.Abp.Autofac; using Volo.Abp.BackgroundJobs; using Volo.Abp.Modularity; @@ -17,6 +19,20 @@ namespace EventHub.DbMigrator public override void ConfigureServices(ServiceConfigurationContext context) { Configure(options => options.IsJobExecutionEnabled = false); + +#if DEBUG + Configure(options => + { + options.Infos.Add(new PlanInfoDefinition + { + PlanType = OrganizationPlanType.Free, + Description = "This is a default plan.", + IsActive = true, + Price = 0, + IsExtendable = false + }); + }); } +#endif } } diff --git a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs index 3794fac..4aac5d3 100644 --- a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs +++ b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs @@ -9,7 +9,7 @@ using Volo.Abp.Timing; namespace EventHub.Events.Registrations { - public class EventRegistrationManager : IDomainService + public class EventRegistrationManager : DomainService { private readonly IEventRegistrationRepository _eventRegistrationRepository; private readonly IGuidGenerator _guidGenerator; diff --git a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs index e5cd049..db1bb07 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs @@ -26,7 +26,7 @@ public class PlanFeatureManager : ITransientDependency _eventRegistrationRepository = eventRegistrationRepository; } - public async Task CanCreateNewEvent(Guid organizationId) + public virtual async Task CanCreateNewEvent(Guid organizationId) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId); @@ -35,7 +35,7 @@ public class PlanFeatureManager : ITransientDependency return currentPlanOfOrganization.Feature.MaxAllowedEventsCount >= totalEventCountByOrganization; } - public async Task CanAddNewTrack(Event @event) + public virtual async Task CanAddNewTrack(Event @event) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); @@ -44,7 +44,7 @@ public class PlanFeatureManager : ITransientDependency return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent >= tracksCount; } - public async Task CanRegisterToEvent(Event @event) + public virtual async Task CanRegisterToEvent(Event @event) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); diff --git a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs index 8c167cd..78ad9fa 100644 --- a/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs +++ b/src/EventHub.HttpApi.Host/EventHubHttpApiHostModule.cs @@ -203,7 +203,6 @@ namespace EventHub .Validate(PlanInfoDefinition.IsValid, "PlanInfoDefinition is not valid!"); var planInfos = context.Services.GetRequiredServiceLazy>>(); - Configure(options => { options.AddPlanInfos(planInfos.Value.Value); diff --git a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs index 786282c..ce8c7a4 100644 --- a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs +++ b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs @@ -1,5 +1,6 @@ using System; using System.Threading.Tasks; +using EventHub.Organizations.Plans; using NSubstitute; using Shouldly; using Volo.Abp.Guids; @@ -37,7 +38,7 @@ namespace EventHub.Events.Registrations [Fact] public async Task RegisterAsync() { - var evnt = new Event( + var @event = new Event( Guid.NewGuid(), Guid.NewGuid(), "1a8j3v0d", @@ -50,23 +51,26 @@ namespace EventHub.Events.Registrations var user = new IdentityUser(Guid.NewGuid(), "john", "john@abp.io"); var repository = Substitute.For(); - repository.ExistsAsync(evnt.Id, user.Id).Returns(Task.FromResult(false)); + repository.ExistsAsync(@event.Id, user.Id).Returns(Task.FromResult(false)); var guidGenerator = SimpleGuidGenerator.Instance; var clock = Substitute.For(); clock.Now.Returns(DateTime.Now); + var planFeatureManager = Substitute.For(null, null, null, null); + planFeatureManager.CanRegisterToEvent(@event).Returns(Task.FromResult(true)); + var registrationManager = new EventRegistrationManager( - repository, guidGenerator, clock, null + repository, guidGenerator, clock, planFeatureManager ); - await registrationManager.RegisterAsync(evnt, user); + await registrationManager.RegisterAsync(@event, user); await repository .Received() .InsertAsync( - Arg.Is(er => er.EventId == evnt.Id && er.UserId == user.Id) + Arg.Is(er => er.EventId == @event.Id && er.UserId == user.Id) ); } } diff --git a/test/EventHub.TestBase/EventHubTestBaseModule.cs b/test/EventHub.TestBase/EventHubTestBaseModule.cs index 9dd6796..2dfacaf 100644 --- a/test/EventHub.TestBase/EventHubTestBaseModule.cs +++ b/test/EventHub.TestBase/EventHubTestBaseModule.cs @@ -1,4 +1,6 @@ using System; +using EventHub.Organizations; +using EventHub.Organizations.Plans; using Microsoft.Extensions.DependencyInjection; using Volo.Abp; using Volo.Abp.Authorization; @@ -40,6 +42,18 @@ namespace EventHub }); context.Services.AddAlwaysAllowAuthorization(); + + Configure(options => + { + options.Infos.Add(new PlanInfoDefinition + { + PlanType = OrganizationPlanType.Free, + Description = "This is a default plan.", + IsActive = true, + Price = 0, + IsExtendable = false + }); + }); } public override void OnApplicationInitialization(ApplicationInitializationContext context) From 671fc5730dd676f3200b36a6e447e32c31a9d8f6 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Tue, 8 Feb 2022 16:42:37 +0300 Subject: [PATCH 5/9] Update Organization.cs --- src/EventHub.Domain/Organizations/Organization.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EventHub.Domain/Organizations/Organization.cs b/src/EventHub.Domain/Organizations/Organization.cs index e38c2a2..808216e 100644 --- a/src/EventHub.Domain/Organizations/Organization.cs +++ b/src/EventHub.Domain/Organizations/Organization.cs @@ -87,12 +87,12 @@ namespace EventHub.Organizations return this; } - SetPlanType(OrganizationPlanType.Free, DateTime.Today); + SetPlanType(OrganizationPlanType.Free); return this; } - private Organization SetPlanType(OrganizationPlanType planType, DateTime endDate) + private Organization SetPlanType(OrganizationPlanType planType, DateTime? endDate = null) { if (!Enum.IsDefined(typeof(OrganizationPlanType), planType)) { From 6459dbe90ab7e91bb2c1645879a51cf0018ff5bc Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Tue, 8 Feb 2022 17:04:08 +0300 Subject: [PATCH 6/9] Consider the year in the CanCreateNewEvent method --- .../Organizations/FeatureOfPlanDefinitionDto.cs | 2 +- .../Organizations/Plans/FeatureOfPlanDefinition.cs | 2 +- src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs | 4 ++-- src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs | 2 +- src/EventHub.HttpApi.Host/appsettings.json | 2 +- src/EventHub.Web/Pages/Pricing.cshtml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs b/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs index d4e1e0d..42cef87 100644 --- a/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs +++ b/src/EventHub.Application.Contracts/Organizations/FeatureOfPlanDefinitionDto.cs @@ -4,7 +4,7 @@ namespace EventHub.Organizations; public class FeatureOfPlanDefinitionDto { - public uint? MaxAllowedEventsCount { get; set; } + public uint? MaxAllowedEventsCountInOneYear { get; set; } public uint? MaxAllowedTracksCountInOneEvent { get; set; } diff --git a/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs index c9724ac..c693856 100644 --- a/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs @@ -8,7 +8,7 @@ public class FeatureOfPlanDefinition { [CanBeNull] [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] - public uint? MaxAllowedEventsCount { get; set; } + public uint? MaxAllowedEventsCountInOneYear { get; set; } [CanBeNull] [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] diff --git a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs index db1bb07..6074015 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs @@ -30,9 +30,9 @@ public class PlanFeatureManager : ITransientDependency { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId); - var totalEventCountByOrganization = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId); + var totalEventCountByOrganization = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId && x.CreationTime.Year < DateTime.Now.Year + 1 && x.CreationTime.Year > DateTime.Now.Year - 1); totalEventCountByOrganization++; - return currentPlanOfOrganization.Feature.MaxAllowedEventsCount >= totalEventCountByOrganization; + return currentPlanOfOrganization.Feature.MaxAllowedEventsCountInOneYear >= totalEventCountByOrganization; } public virtual async Task CanAddNewTrack(Event @event) diff --git a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs index 459ceca..22043f1 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs @@ -36,7 +36,7 @@ public class PlanInfoDefinition { Feature = new FeatureOfPlanDefinition { - MaxAllowedEventsCount = uint.MaxValue, + MaxAllowedEventsCountInOneYear = uint.MaxValue, MaxAllowedTracksCountInOneEvent = uint.MaxValue, MaxAllowedAttendeesCountInOneEvent = uint.MaxValue, AdditionalFeatureInfos = new List() diff --git a/src/EventHub.HttpApi.Host/appsettings.json b/src/EventHub.HttpApi.Host/appsettings.json index 6de340b..34c8801 100644 --- a/src/EventHub.HttpApi.Host/appsettings.json +++ b/src/EventHub.HttpApi.Host/appsettings.json @@ -27,7 +27,7 @@ "Price": 0, "IsExtendable": false, "Feature": { - "MaxAllowedEventsCount": 2, + "MaxAllowedEventsCountInOneYear": 2, "MaxAllowedTracksCountInOneEvent": 2, "MaxAllowedAttendeesCountInOneEvent": 1000 } diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index 687b433..5aa1ed7 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -25,7 +25,7 @@ @plan.Price
      -
    • @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedEventsCount) events in a year.
    • +
    • @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedEventsCountInOneYear) events in a year.
    • Up to @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedTracksCountInOneEvent).ToLower() tracks for an event.
    • Up to @PlanInfoHelper.GetFeatureInfoByCount(plan.Feature.MaxAllowedAttendeesCountInOneEvent).ToLower() attendees per event.
    • @foreach (var additionalFeatureInfo in plan.Feature.AdditionalFeatureInfos) From 3ea33cb607e591befc1a556ca4ff81b98bbb6c19 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Thu, 3 Mar 2022 17:47:55 +0300 Subject: [PATCH 7/9] Update Pricing.cshtml --- src/EventHub.Web/Pages/Pricing.cshtml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/EventHub.Web/Pages/Pricing.cshtml b/src/EventHub.Web/Pages/Pricing.cshtml index 5aa1ed7..e6027ac 100644 --- a/src/EventHub.Web/Pages/Pricing.cshtml +++ b/src/EventHub.Web/Pages/Pricing.cshtml @@ -33,7 +33,7 @@
    • @additionalFeatureInfo
    • }
    - @if (Model.Organization.PlanType == plan.PlanType && plan.Price > 0) + @if (Model.Organization.PlanType == plan.PlanType && plan.Price > 0 && plan.IsExtendable) {
    @@ -41,7 +41,7 @@ Extend Now
    } - else if (plan.IsExtendable) + else if (plan.Price > 0) {
    From e3b65b2cf1a54d8644b02fbd0236c2a28638d186 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Thu, 3 Mar 2022 17:48:03 +0300 Subject: [PATCH 8/9] fix(organization): null exception occurs when setting plan to free --- src/EventHub.Domain/Organizations/Organization.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/EventHub.Domain/Organizations/Organization.cs b/src/EventHub.Domain/Organizations/Organization.cs index 808216e..4e42a26 100644 --- a/src/EventHub.Domain/Organizations/Organization.cs +++ b/src/EventHub.Domain/Organizations/Organization.cs @@ -98,8 +98,12 @@ namespace EventHub.Organizations { throw new AbpValidationException(); } + + if (planType != OrganizationPlanType.Free) + { + Check.NotNull(endDate, nameof(endDate)); + } - Check.NotNull(endDate, nameof(endDate)); PlanType = planType; PaidEnrollmentEndDate = endDate; From 37f0ce4837efb8d724483ca8ba44634fb448e532 Mon Sep 17 00:00:00 2001 From: berkansasmaz Date: Thu, 3 Mar 2022 19:05:15 +0300 Subject: [PATCH 9/9] refactor(organization.plan): improvements after review --- .../Organizations/OrganizationAppService.cs | 13 +++---- ...OrganizationPaidEnrollmentEndDateWorker.cs | 24 +++++-------- src/EventHub.Domain/Events/EventManager.cs | 4 +-- .../Registrations/EventRegistrationManager.cs | 2 +- .../Plans/FeatureOfPlanDefinition.cs | 11 ++++-- .../Plans/IPlanInfoDefinitionStore.cs | 11 ------ .../Organizations/Plans/PlanFeatureManager.cs | 34 +++++++++---------- .../Organizations/Plans/PlanInfoDefinition.cs | 6 ++-- .../Plans/PlanInfoDefinitionStore.cs | 27 --------------- .../Organizations/Plans/PlanInfoOptions.cs | 11 ++++++ .../EventRegistrationManager_UnitTests.cs | 4 +-- 11 files changed, 60 insertions(+), 87 deletions(-) delete mode 100644 src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs delete mode 100644 src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs diff --git a/src/EventHub.Application/Organizations/OrganizationAppService.cs b/src/EventHub.Application/Organizations/OrganizationAppService.cs index 4f9f3dd..f61c5da 100644 --- a/src/EventHub.Application/Organizations/OrganizationAppService.cs +++ b/src/EventHub.Application/Organizations/OrganizationAppService.cs @@ -7,6 +7,7 @@ using EventHub.Organizations.PaymentRequests; using EventHub.Organizations.Plans; using EventHub.Users; using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; using Volo.Abp.Application.Dtos; using Volo.Abp.Authorization; using Volo.Abp.BlobStoring; @@ -23,22 +24,22 @@ namespace EventHub.Organizations private readonly OrganizationManager _organizationManager; private readonly IBlobContainer _organizationBlobContainer; private readonly IUserRepository _userRepository; - private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; + private readonly IOptions _planInfoOptions; public OrganizationAppService( IRepository organizationRepository, IOrganizationMembershipRepository organizationMembershipsRepository, OrganizationManager organizationManager, IBlobContainer organizationBlobContainer, - IUserRepository userRepository, - IPlanInfoDefinitionStore planInfoDefinitionStore) + IUserRepository userRepository, + IOptions planInfoOptions) { _organizationRepository = organizationRepository; _organizationMembershipsRepository = organizationMembershipsRepository; _organizationManager = organizationManager; _organizationBlobContainer = organizationBlobContainer; _userRepository = userRepository; - _planInfoDefinitionStore = planInfoDefinitionStore; + _planInfoOptions = planInfoOptions; } [Authorize] @@ -171,8 +172,8 @@ namespace EventHub.Organizations [Authorize] public async Task> GetPlanInfosAsync() { - var planInfoDefinitions = await _planInfoDefinitionStore.GetPlanInfosAsync(); - return ObjectMapper.Map, List>(planInfoDefinitions); + var planInfoDefinitions = _planInfoOptions.Value.GetPlanInfos(); + return await Task.FromResult(ObjectMapper.Map, List>(planInfoDefinitions)); } private async Task SaveProfilePictureAsync(Guid id, IRemoteStreamContent streamContent) diff --git a/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs b/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs index c4316b5..910c602 100644 --- a/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs +++ b/src/EventHub.BackgroundServices/Organizations/OrganizationPaidEnrollmentEndDateWorker.cs @@ -41,7 +41,8 @@ namespace EventHub.Organizations var queryable = await organizationRepository.GetQueryableAsync(); var query = queryable.Where(x => x.IsSendPaidEnrollmentReminderEmail == false && - x.PaidEnrollmentEndDate <= oneMonthLater + x.PaidEnrollmentEndDate <= oneMonthLater && + x.PlanType != OrganizationPlanType.Free ); var organizations = await asyncExecuter.ToListAsync(query); @@ -50,16 +51,13 @@ namespace EventHub.Organizations { try { - if (organization.PlanType != OrganizationPlanType.Free) + if (organization.PaidEnrollmentEndDate >= clock.Now) { - if (organization.PaidEnrollmentEndDate >= clock.Now) - { - await organizationPlanEndDateNotifier.NotifyAsync(organization); - } - - organization.IsSendPaidEnrollmentReminderEmail = true; - await organizationRepository.UpdateAsync(organization); + await organizationPlanEndDateNotifier.NotifyAsync(organization); } + + organization.IsSendPaidEnrollmentReminderEmail = true; + await organizationRepository.UpdateAsync(organization); } catch (Exception ex) { @@ -72,7 +70,8 @@ namespace EventHub.Organizations { var queryable = await organizationRepository.GetQueryableAsync(); var query = queryable.Where(x => - x.PaidEnrollmentEndDate >= clock.Now + x.PaidEnrollmentEndDate > clock.Now.Date && + x.PlanType != OrganizationPlanType.Free ); var organizations = await asyncExecuter.ToListAsync(query); @@ -81,11 +80,6 @@ namespace EventHub.Organizations { try { - if (organization.PlanType == OrganizationPlanType.Free) - { - continue; - } - organization.SetFreeToPlanType(); organization.IsSendPaidEnrollmentReminderEmail = false; await organizationRepository.UpdateAsync(organization); diff --git a/src/EventHub.Domain/Events/EventManager.cs b/src/EventHub.Domain/Events/EventManager.cs index 24b18a8..ed58c98 100644 --- a/src/EventHub.Domain/Events/EventManager.cs +++ b/src/EventHub.Domain/Events/EventManager.cs @@ -35,7 +35,7 @@ namespace EventHub.Events DateTime endTime, string description) { - if (!await _planFeatureManager.CanCreateNewEvent(organization.Id)) + if (!await _planFeatureManager.CanCreateNewEventAsync(organization.Id)) { throw new BusinessException(EventHubErrorCodes.CannotCreateNewEvent); } @@ -53,7 +53,7 @@ namespace EventHub.Events public async Task AddTrackAsync(Event @event, string name) { - if (!await _planFeatureManager.CanAddNewTrack(@event)) + if (!await _planFeatureManager.CanAddNewTrackAsync(@event)) { throw new BusinessException(EventHubErrorCodes.CannotAddNewTrack); } diff --git a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs index 4aac5d3..bc41542 100644 --- a/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs +++ b/src/EventHub.Domain/Events/Registrations/EventRegistrationManager.cs @@ -52,7 +52,7 @@ namespace EventHub.Events.Registrations } } - if (!await _planFeatureManager.CanRegisterToEvent(@event)) + if (!await _planFeatureManager.CanRegisterToEventAsync(@event)) { throw new BusinessException(EventHubErrorCodes.CannotRegisterToEvent); } diff --git a/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs index c693856..bf25258 100644 --- a/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/FeatureOfPlanDefinition.cs @@ -7,17 +7,22 @@ namespace EventHub.Organizations.Plans; public class FeatureOfPlanDefinition { [CanBeNull] - [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(0, uint.MaxValue)] public uint? MaxAllowedEventsCountInOneYear { get; set; } [CanBeNull] - [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(0, uint.MaxValue)] public uint? MaxAllowedTracksCountInOneEvent { get; set; } [CanBeNull] - [Range(0, uint.MaxValue, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(0, uint.MaxValue)] public uint? MaxAllowedAttendeesCountInOneEvent { get; set; } [NotNull] public List AdditionalFeatureInfos { get; set; } + + public FeatureOfPlanDefinition() + { + AdditionalFeatureInfos = new List(); + } } diff --git a/src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs deleted file mode 100644 index 4572650..0000000 --- a/src/EventHub.Domain/Organizations/Plans/IPlanInfoDefinitionStore.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace EventHub.Organizations.Plans; - -public interface IPlanInfoDefinitionStore -{ - Task> GetPlanInfosAsync(); - - Task GetPlanInfoByTypeAsync(OrganizationPlanType type); -} diff --git a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs index 6074015..be95b4c 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanFeatureManager.cs @@ -2,60 +2,60 @@ using System; using System.Threading.Tasks; using EventHub.Events; using EventHub.Events.Registrations; +using Microsoft.Extensions.Options; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Timing; namespace EventHub.Organizations.Plans; public class PlanFeatureManager : ITransientDependency { - private readonly IPlanInfoDefinitionStore _planInfoDefinitionStore; + private readonly IOptions _planInfoOptions; private readonly IRepository _organizationRepository; private readonly IRepository _eventRepository; private readonly IEventRegistrationRepository _eventRegistrationRepository; - + private readonly IClock _clock; public PlanFeatureManager( - IPlanInfoDefinitionStore planInfoDefinitionStore, + IOptions planInfoOptions, IRepository organizationRepository, IRepository eventRepository, - IEventRegistrationRepository eventRegistrationRepository) + IEventRegistrationRepository eventRegistrationRepository, + IClock clock) { - _planInfoDefinitionStore = planInfoDefinitionStore; + _planInfoOptions = planInfoOptions; _organizationRepository = organizationRepository; _eventRepository = eventRepository; _eventRegistrationRepository = eventRegistrationRepository; + _clock = clock; } - public virtual async Task CanCreateNewEvent(Guid organizationId) + public virtual async Task CanCreateNewEventAsync(Guid organizationId) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(organizationId); - - var totalEventCountByOrganization = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId && x.CreationTime.Year < DateTime.Now.Year + 1 && x.CreationTime.Year > DateTime.Now.Year - 1); - totalEventCountByOrganization++; - return currentPlanOfOrganization.Feature.MaxAllowedEventsCountInOneYear >= totalEventCountByOrganization; + var totalEventCountByOrganizationInThisYear = await _eventRepository.CountAsync(x => x.OrganizationId == organizationId && x.CreationTime.Year == _clock.Now.Year); + return currentPlanOfOrganization.Feature.MaxAllowedEventsCountInOneYear > totalEventCountByOrganizationInThisYear; } - public virtual async Task CanAddNewTrack(Event @event) + public virtual async Task CanAddNewTrackAsync(Event @event) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); var tracksCount = @event.Tracks.Count; - tracksCount++; - return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent >= tracksCount; + return currentPlanOfOrganization.Feature.MaxAllowedTracksCountInOneEvent > tracksCount; } - public virtual async Task CanRegisterToEvent(Event @event) + public virtual async Task CanRegisterToEventAsync(Event @event) { var currentPlanOfOrganization = await GetCurrentPlanByOrganizationIdAsync(@event.OrganizationId); var attendeesCount = await _eventRegistrationRepository.CountAsync(x => x.EventId == @event.Id); - attendeesCount++; - return currentPlanOfOrganization.Feature.MaxAllowedAttendeesCountInOneEvent >= attendeesCount; + return currentPlanOfOrganization.Feature.MaxAllowedAttendeesCountInOneEvent > attendeesCount; } private async Task GetCurrentPlanByOrganizationIdAsync(Guid organizationId) { var organization = await _organizationRepository.GetAsync(organizationId); - return await _planInfoDefinitionStore.GetPlanInfoByTypeAsync(organization.PlanType); + return _planInfoOptions.Value.GetPlanInfoByType(organization.PlanType); } } diff --git a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs index 22043f1..4735ca4 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinition.cs @@ -17,17 +17,17 @@ public class PlanInfoDefinition public bool IsActive { get; set; } - [Range(1.0, 100.0, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(1.0, 100.0)] public decimal Price { get; set; } = 1.0M; public bool IsExtendable { get; set; } [CanBeNull] - [Range(0, 12, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(0, 12)] public int? CanBeExtendedAfterHowManyMonths { get; set; } [CanBeNull] - [Range(1, 24, ErrorMessage = "Value for {0} must be between {1} and {2}.")] + [Range(1, 24)] public int? OnePaidEnrollmentPeriodAsMonth { get; set; } = 12; public FeatureOfPlanDefinition Feature { get; set; } diff --git a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs deleted file mode 100644 index 1f1b603..0000000 --- a/src/EventHub.Domain/Organizations/Plans/PlanInfoDefinitionStore.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; -using Volo.Abp.DependencyInjection; - -namespace EventHub.Organizations.Plans; - -public class PlanInfoDefinitionStore : IPlanInfoDefinitionStore, ITransientDependency -{ - protected PlanInfoOptions PlanInfoOptions { get; } - - public PlanInfoDefinitionStore(IOptions planInfoOptions) - { - PlanInfoOptions = planInfoOptions.Value; - } - - public async Task> GetPlanInfosAsync() - { - return await Task.FromResult(PlanInfoOptions.Infos); - } - - public async Task GetPlanInfoByTypeAsync(OrganizationPlanType type) - { - return await Task.FromResult(PlanInfoOptions.Infos.SingleOrDefault(x => x.PlanType == type)); - } -} diff --git a/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs b/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs index 771c825..46671a7 100644 --- a/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs +++ b/src/EventHub.Domain/Organizations/Plans/PlanInfoOptions.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; namespace EventHub.Organizations.Plans; @@ -27,4 +28,14 @@ public class PlanInfoOptions return this; } + + public List GetPlanInfos() + { + return Infos.Where(x => x.IsActive).ToList(); + } + + public PlanInfoDefinition GetPlanInfoByType(OrganizationPlanType type) + { + return Infos.SingleOrDefault(x => x.IsActive && x.PlanType == type); + } } diff --git a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs index ce8c7a4..4b59f93 100644 --- a/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs +++ b/test/EventHub.Domain.Tests/Events/Registrations/EventRegistrationManager_UnitTests.cs @@ -58,8 +58,8 @@ namespace EventHub.Events.Registrations var clock = Substitute.For(); clock.Now.Returns(DateTime.Now); - var planFeatureManager = Substitute.For(null, null, null, null); - planFeatureManager.CanRegisterToEvent(@event).Returns(Task.FromResult(true)); + var planFeatureManager = Substitute.For(null, null, null, null, null); + planFeatureManager.CanRegisterToEventAsync(@event).Returns(Task.FromResult(true)); var registrationManager = new EventRegistrationManager( repository, guidGenerator, clock, planFeatureManager