1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-04 01:22:50 -05:00

[AC-1910] Allocate seats to a provider organization (#3936)

* Add endpoint to update a provider organization's seats for consolidated billing.

* Fixed failing tests
This commit is contained in:
Alex Morask
2024-03-29 11:18:10 -04:00
committed by GitHub
parent c53e5eeab3
commit e2cb406a95
28 changed files with 1108 additions and 68 deletions

View File

@ -1,9 +1,22 @@
using Bit.Core.Billing.Models;
using Bit.Core.AdminConsole.Entities.Provider;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.Billing.Models;
using Bit.Core.Enums;
namespace Bit.Core.Billing.Queries;
public interface IProviderBillingQueries
{
/// <summary>
/// Retrieves the number of seats an MSP has assigned to its client organizations with a specified <paramref name="planType"/>.
/// </summary>
/// <param name="providerId">The ID of the MSP to retrieve the assigned seat total for.</param>
/// <param name="planType">The type of plan to retrieve the assigned seat total for.</param>
/// <returns>An <see cref="int"/> representing the number of seats the provider has assigned to its client organizations with the specified <paramref name="planType"/>.</returns>
/// <exception cref="BillingException">Thrown when the provider represented by the <paramref name="providerId"/> is <see langword="null"/>.</exception>
/// <exception cref="BillingException">Thrown when the provider represented by the <paramref name="providerId"/> has <see cref="Provider.Type"/> <see cref="ProviderType.Reseller"/>.</exception>
Task<int> GetAssignedSeatTotalForPlanOrThrow(Guid providerId, PlanType planType);
/// <summary>
/// Retrieves a provider's billing subscription data.
/// </summary>

View File

@ -1,17 +1,53 @@
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Models;
using Bit.Core.Billing.Repositories;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.Extensions.Logging;
using Stripe;
using static Bit.Core.Billing.Utilities;
namespace Bit.Core.Billing.Queries.Implementations;
public class ProviderBillingQueries(
ILogger<ProviderBillingQueries> logger,
IProviderOrganizationRepository providerOrganizationRepository,
IProviderPlanRepository providerPlanRepository,
IProviderRepository providerRepository,
ISubscriberQueries subscriberQueries) : IProviderBillingQueries
{
public async Task<int> GetAssignedSeatTotalForPlanOrThrow(
Guid providerId,
PlanType planType)
{
var provider = await providerRepository.GetByIdAsync(providerId);
if (provider == null)
{
logger.LogError(
"Could not find provider ({ID}) when retrieving assigned seat total",
providerId);
throw ContactSupport();
}
if (provider.Type == ProviderType.Reseller)
{
logger.LogError("Assigned seats cannot be retrieved for reseller-type provider ({ID})", providerId);
throw ContactSupport("Consolidated billing does not support reseller-type providers");
}
var providerOrganizations = await providerOrganizationRepository.GetManyDetailsByProviderAsync(providerId);
var plan = StaticStore.GetPlan(planType);
return providerOrganizations
.Where(providerOrganization => providerOrganization.Plan == plan.Name)
.Sum(providerOrganization => providerOrganization.Seats ?? 0);
}
public async Task<ProviderSubscriptionData> GetSubscriptionData(Guid providerId)
{
var provider = await providerRepository.GetByIdAsync(providerId);
@ -25,6 +61,13 @@ public class ProviderBillingQueries(
return null;
}
if (provider.Type == ProviderType.Reseller)
{
logger.LogError("Subscription data cannot be retrieved for reseller-type provider ({ID})", providerId);
throw ContactSupport("Consolidated billing does not support reseller-type providers");
}
var subscription = await subscriberQueries.GetSubscription(provider, new SubscriptionGetOptions
{
Expand = ["customer"]
@ -38,7 +81,7 @@ public class ProviderBillingQueries(
var providerPlans = await providerPlanRepository.GetByProviderId(providerId);
var configuredProviderPlans = providerPlans
.Where(providerPlan => providerPlan.Configured)
.Where(providerPlan => providerPlan.IsConfigured())
.Select(ConfiguredProviderPlan.From)
.ToList();