diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs index b6773f0bd431..48ea903adaf3 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs @@ -392,7 +392,9 @@ public async Task AddOrganization(Guid providerId, Guid organizationId, string k var organization = await _organizationRepository.GetByIdAsync(organizationId); - ThrowOnInvalidPlanType(organization.PlanType); + var provider = await _providerRepository.GetByIdAsync(providerId); + + ThrowOnInvalidPlanType(provider.Type, organization.PlanType); if (organization.UseSecretsManager) { @@ -407,8 +409,6 @@ public async Task AddOrganization(Guid providerId, Guid organizationId, string k Key = key, }; - var provider = await _providerRepository.GetByIdAsync(providerId); - await ApplyProviderPriceRateAsync(organization, provider); await _providerOrganizationRepository.CreateAsync(providerOrganization); @@ -547,7 +547,7 @@ public async Task CreateOrganizationAsync(Guid providerId, var consolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling) && provider.IsBillable(); - ThrowOnInvalidPlanType(organizationSignup.Plan, consolidatedBillingEnabled); + ThrowOnInvalidPlanType(provider.Type, organizationSignup.Plan, consolidatedBillingEnabled); var (organization, _, defaultCollection) = consolidatedBillingEnabled ? await _organizationService.SignupClientAsync(organizationSignup) @@ -687,11 +687,27 @@ private async Task HasConfirmedProviderAdminExceptAsync(Guid providerId, I return confirmedOwnersIds.Except(providerUserIds).Any(); } - private void ThrowOnInvalidPlanType(PlanType requestedType, bool consolidatedBillingEnabled = false) + private void ThrowOnInvalidPlanType(ProviderType providerType, PlanType requestedType, bool consolidatedBillingEnabled = false) { - if (consolidatedBillingEnabled && requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly)) + if (consolidatedBillingEnabled) { - throw new BadRequestException($"Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed."); + switch (providerType) + { + case ProviderType.Msp: + if (requestedType is not (PlanType.TeamsMonthly or PlanType.EnterpriseMonthly)) + { + throw new BadRequestException($"Managed Service Providers cannot manage organizations with the plan type {requestedType}. Only Teams (Monthly) and Enterprise (Monthly) are allowed."); + } + break; + case ProviderType.MultiOrganizationEnterprise: + if (requestedType is not (PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually)) + { + throw new BadRequestException($"Multi-organization Enterprise Providers cannot manage organizations with the plan type {requestedType}. Only Enterprise (Monthly) and Enterprise (Annually) are allowed."); + } + break; + default: + throw new BadRequestException($"Unsupported provider type {providerType}."); + } } if (ProviderDisallowedOrganizationTypes.Contains(requestedType)) diff --git a/src/Api/Billing/Models/Requests/CreateClientOrganizationRequestBody.cs b/src/Api/Billing/Models/Requests/CreateClientOrganizationRequestBody.cs index 39b2e3323271..95836151d6b2 100644 --- a/src/Api/Billing/Models/Requests/CreateClientOrganizationRequestBody.cs +++ b/src/Api/Billing/Models/Requests/CreateClientOrganizationRequestBody.cs @@ -12,7 +12,7 @@ public class CreateClientOrganizationRequestBody [Required(ErrorMessage = "'ownerEmail' must be provided")] public string OwnerEmail { get; set; } - [EnumMatches(PlanType.TeamsMonthly, PlanType.EnterpriseMonthly, ErrorMessage = "'planType' must be Teams (Monthly) or Enterprise (Monthly)")] + [EnumMatches(PlanType.TeamsMonthly, PlanType.EnterpriseMonthly, PlanType.EnterpriseAnnually, ErrorMessage = "'planType' must be Teams (Monthly), Enterprise (Monthly) or Enterprise (Annually)")] public PlanType PlanType { get; set; } [Range(1, int.MaxValue, ErrorMessage = "'seats' must be greater than 0")] diff --git a/src/Core/Billing/Extensions/BillingExtensions.cs b/src/Core/Billing/Extensions/BillingExtensions.cs index 21974b318534..02e8de9244c4 100644 --- a/src/Core/Billing/Extensions/BillingExtensions.cs +++ b/src/Core/Billing/Extensions/BillingExtensions.cs @@ -43,5 +43,5 @@ setupIntent is }; public static bool SupportsConsolidatedBilling(this PlanType planType) - => planType is PlanType.TeamsMonthly or PlanType.EnterpriseMonthly; + => planType is PlanType.TeamsMonthly or PlanType.EnterpriseMonthly or PlanType.EnterpriseAnnually; }