From 15eea77d66ae0ff2718f9896ba4c2f2f421ebaac Mon Sep 17 00:00:00 2001 From: Alex Morask <144709477+amorask-bitwarden@users.noreply.github.com> Date: Tue, 19 Mar 2024 09:36:25 -0400 Subject: [PATCH] [AC-2284] Set organization billing email to MSP billing email when linked (#3897) * Set org billing email to provider billing email when added to provider * Remove anonymous args for test assertions --- .../AdminConsole/Services/ProviderService.cs | 17 +- .../Services/ProviderServiceTests.cs | 158 +++++++++++------- 2 files changed, 110 insertions(+), 65 deletions(-) diff --git a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs index 3edde8a6ae..bad44cb3c2 100644 --- a/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs +++ b/bitwarden_license/src/Commercial.Core/AdminConsole/Services/ProviderService.cs @@ -17,6 +17,7 @@ using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Utilities; using Microsoft.AspNetCore.DataProtection; +using Stripe; namespace Bit.Commercial.Core.AdminConsole.Services; @@ -374,8 +375,18 @@ public class ProviderService : IProviderService Key = key, }; - await ApplyProviderPriceRateAsync(organizationId, providerId); + var provider = await _providerRepository.GetByIdAsync(providerId); + + await ApplyProviderPriceRateAsync(organization, provider); await _providerOrganizationRepository.CreateAsync(providerOrganization); + + organization.BillingEmail = provider.BillingEmail; + await _organizationRepository.ReplaceAsync(organization); + await _stripeAdapter.CustomerUpdateAsync(organization.GatewayCustomerId, new CustomerUpdateOptions + { + Email = provider.BillingEmail + }); + await _eventService.LogProviderOrganizationEventAsync(providerOrganization, EventType.ProviderOrganization_Added); } @@ -400,16 +411,14 @@ public class ProviderService : IProviderService await _eventService.LogProviderOrganizationEventsAsync(insertedProviderOrganizations.Select(ipo => (ipo, EventType.ProviderOrganization_Added, (DateTime?)null))); } - private async Task ApplyProviderPriceRateAsync(Guid organizationId, Guid providerId) + private async Task ApplyProviderPriceRateAsync(Organization organization, Provider provider) { - var provider = await _providerRepository.GetByIdAsync(providerId); // if a provider was created before Nov 6, 2023.If true, the organization plan assigned to that provider is updated to a 2020 plan. if (provider.CreationDate >= Constants.ProviderCreatedPriorNov62023) { return; } - var organization = await _organizationRepository.GetByIdAsync(organizationId); var subscriptionItem = await GetSubscriptionItemAsync(organization.GatewaySubscriptionId, GetStripeSeatPlanId(organization.PlanType)); var extractedPlanType = PlanTypeMappings(organization); if (subscriptionItem != null) diff --git a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs index b503d0d5a7..0ab8c588f3 100644 --- a/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs +++ b/bitwarden_license/test/Commercial.Core.Test/AdminConsole/Services/ProviderServiceTests.cs @@ -458,17 +458,112 @@ public class ProviderServiceTests { organization.PlanType = PlanType.EnterpriseAnnually; + var providerRepository = sutProvider.GetDependency(); + providerRepository.GetByIdAsync(provider.Id).Returns(provider); + + var providerOrganizationRepository = sutProvider.GetDependency(); + providerOrganizationRepository.GetByOrganizationId(organization.Id).ReturnsNull(); + + var organizationRepository = sutProvider.GetDependency(); + organizationRepository.GetByIdAsync(organization.Id).Returns(organization); + + await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); + + await providerOrganizationRepository.Received(1) + .CreateAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key)); + + await organizationRepository.Received(1) + .ReplaceAsync(Arg.Is(org => org.BillingEmail == provider.BillingEmail)); + + await sutProvider.GetDependency().Received(1).CustomerUpdateAsync( + organization.GatewayCustomerId, + Arg.Is(options => options.Email == provider.BillingEmail)); + + await sutProvider.GetDependency() + .Received().LogProviderOrganizationEventAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key), + EventType.ProviderOrganization_Added); + } + + [Theory, BitAutoData] + public async Task AddOrganization_CreateAfterNov62023_PlanTypeDoesNotUpdated(Provider provider, Organization organization, string key, + SutProvider sutProvider) + { + provider.Type = ProviderType.Msp; + + sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); + + var providerOrganizationRepository = sutProvider.GetDependency(); + var expectedPlanType = PlanType.EnterpriseAnnually; + organization.PlanType = PlanType.EnterpriseAnnually; + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + + await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); + + await providerOrganizationRepository.Received(1) + .CreateAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key)); + + await sutProvider.GetDependency() + .Received().LogProviderOrganizationEventAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key), + EventType.ProviderOrganization_Added); + + Assert.Equal(organization.PlanType, expectedPlanType); + } + + [Theory, BitAutoData] + public async Task AddOrganization_CreateBeforeNov62023_PlanTypeUpdated(Provider provider, Organization organization, string key, + SutProvider sutProvider) + { + var newCreationDate = new DateTime(2023, 11, 5); + BackdateProviderCreationDate(provider, newCreationDate); + provider.Type = ProviderType.Msp; + + organization.PlanType = PlanType.EnterpriseAnnually; + organization.Plan = "Enterprise (Annually)"; + + var expectedPlanType = PlanType.EnterpriseAnnually2020; + + var expectedPlanId = "2020-enterprise-org-seat-annually"; + sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); var providerOrganizationRepository = sutProvider.GetDependency(); providerOrganizationRepository.GetByOrganizationId(organization.Id).ReturnsNull(); sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); + var subscriptionItem = GetSubscription(organization.GatewaySubscriptionId); + sutProvider.GetDependency().SubscriptionGetAsync(organization.GatewaySubscriptionId) + .Returns(GetSubscription(organization.GatewaySubscriptionId)); + await sutProvider.GetDependency().SubscriptionUpdateAsync( + organization.GatewaySubscriptionId, SubscriptionUpdateRequest(expectedPlanId, subscriptionItem)); + await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); - await providerOrganizationRepository.ReceivedWithAnyArgs().CreateAsync(default); + await providerOrganizationRepository.Received(1) + .CreateAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key)); + await sutProvider.GetDependency() - .Received().LogProviderOrganizationEventAsync(Arg.Any(), + .Received().LogProviderOrganizationEventAsync(Arg.Is(providerOrganization => + providerOrganization.ProviderId == provider.Id && + providerOrganization.OrganizationId == organization.Id && + providerOrganization.Key == key), EventType.ProviderOrganization_Added); + + Assert.Equal(organization.PlanType, expectedPlanType); } [Theory, BitAutoData] @@ -576,65 +671,6 @@ public class ProviderServiceTests t.First().Item2 == null)); } - [Theory, BitAutoData] - public async Task AddOrganization_CreateAfterNov62023_PlanTypeDoesNotUpdated(Provider provider, Organization organization, string key, - SutProvider sutProvider) - { - provider.Type = ProviderType.Msp; - - sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); - - var providerOrganizationRepository = sutProvider.GetDependency(); - var expectedPlanType = PlanType.EnterpriseAnnually; - organization.PlanType = PlanType.EnterpriseAnnually; - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - - await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); - - await providerOrganizationRepository.ReceivedWithAnyArgs().CreateAsync(default); - await sutProvider.GetDependency() - .Received().LogProviderOrganizationEventAsync(Arg.Any(), - EventType.ProviderOrganization_Added); - Assert.Equal(organization.PlanType, expectedPlanType); - } - - [Theory, BitAutoData] - public async Task AddOrganization_CreateBeforeNov62023_PlanTypeUpdated(Provider provider, Organization organization, string key, - SutProvider sutProvider) - { - var newCreationDate = new DateTime(2023, 11, 5); - BackdateProviderCreationDate(provider, newCreationDate); - provider.Type = ProviderType.Msp; - - organization.PlanType = PlanType.EnterpriseAnnually; - organization.Plan = "Enterprise (Annually)"; - - var expectedPlanType = PlanType.EnterpriseAnnually2020; - - var expectedPlanId = "2020-enterprise-org-seat-annually"; - - sutProvider.GetDependency().GetByIdAsync(provider.Id).Returns(provider); - var providerOrganizationRepository = sutProvider.GetDependency(); - providerOrganizationRepository.GetByOrganizationId(organization.Id).ReturnsNull(); - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - - sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); - var subscriptionItem = GetSubscription(organization.GatewaySubscriptionId); - sutProvider.GetDependency().SubscriptionGetAsync(organization.GatewaySubscriptionId) - .Returns(GetSubscription(organization.GatewaySubscriptionId)); - await sutProvider.GetDependency().SubscriptionUpdateAsync( - organization.GatewaySubscriptionId, SubscriptionUpdateRequest(expectedPlanId, subscriptionItem)); - - await sutProvider.Sut.AddOrganization(provider.Id, organization.Id, key); - - await providerOrganizationRepository.ReceivedWithAnyArgs().CreateAsync(default); - await sutProvider.GetDependency() - .Received().LogProviderOrganizationEventAsync(Arg.Any(), - EventType.ProviderOrganization_Added); - - Assert.Equal(organization.PlanType, expectedPlanType); - } - private static SubscriptionUpdateOptions SubscriptionUpdateRequest(string expectedPlanId, Subscription subscriptionItem) => new() {