Skip to content

Commit

Permalink
Merge branch 'main' into billing/AC-2576/replace-cqrs-services
Browse files Browse the repository at this point in the history
  • Loading branch information
amorask-bitwarden committed May 14, 2024
2 parents 2b95ae0 + e93894a commit 54824b0
Show file tree
Hide file tree
Showing 7 changed files with 199 additions and 155 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public async Task CreateMspAsync(Provider provider, string ownerEmail, int teams
throw new BadRequestException("Invalid owner. Owner must be an existing Bitwarden user.");
}

var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);

if (isConsolidatedBillingEnabled)
{
provider.Gateway = GatewayType.Stripe;
}

await ProviderRepositoryCreateAsync(provider, ProviderStatusType.Pending);

var providerUser = new ProviderUser
Expand All @@ -56,8 +63,6 @@ public async Task CreateMspAsync(Provider provider, string ownerEmail, int teams
Status = ProviderUserStatusType.Confirmed,
};

var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);

if (isConsolidatedBillingEnabled)
{
var providerPlans = new List<ProviderPlan>
Expand Down
39 changes: 25 additions & 14 deletions src/Admin/AdminConsole/Controllers/ProvidersController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Tools.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
Expand All @@ -36,7 +35,6 @@ public class ProvidersController : Controller
private readonly GlobalSettings _globalSettings;
private readonly IApplicationCacheService _applicationCacheService;
private readonly IProviderService _providerService;
private readonly IReferenceEventService _referenceEventService;
private readonly IUserService _userService;
private readonly ICreateProviderCommand _createProviderCommand;
private readonly IFeatureService _featureService;
Expand All @@ -51,7 +49,6 @@ public ProvidersController(
IProviderService providerService,
GlobalSettings globalSettings,
IApplicationCacheService applicationCacheService,
IReferenceEventService referenceEventService,
IUserService userService,
ICreateProviderCommand createProviderCommand,
IFeatureService featureService,
Expand All @@ -65,7 +62,6 @@ public ProvidersController(
_providerService = providerService;
_globalSettings = globalSettings;
_applicationCacheService = applicationCacheService;
_referenceEventService = referenceEventService;
_userService = userService;
_createProviderCommand = createProviderCommand;
_featureService = featureService;
Expand Down Expand Up @@ -104,8 +100,8 @@ public IActionResult Create(int teamsMinimumSeats, int enterpriseMinimumSeats, s
return View(new CreateProviderModel
{
OwnerEmail = ownerEmail,
TeamsMinimumSeats = teamsMinimumSeats,
EnterpriseMinimumSeats = enterpriseMinimumSeats
TeamsMonthlySeatMinimum = teamsMinimumSeats,
EnterpriseMonthlySeatMinimum = enterpriseMinimumSeats
});
}

Expand All @@ -123,8 +119,11 @@ public async Task<IActionResult> Create(CreateProviderModel model)
switch (provider.Type)
{
case ProviderType.Msp:
await _createProviderCommand.CreateMspAsync(provider, model.OwnerEmail, model.TeamsMinimumSeats,
model.EnterpriseMinimumSeats);
await _createProviderCommand.CreateMspAsync(
provider,
model.OwnerEmail,
model.TeamsMonthlySeatMinimum,
model.EnterpriseMonthlySeatMinimum);
break;
case ProviderType.Reseller:
await _createProviderCommand.CreateResellerAsync(provider);
Expand Down Expand Up @@ -167,8 +166,9 @@ public async Task<IActionResult> Edit(Guid id)
return View(new ProviderEditModel(provider, users, providerOrganizations, new List<ProviderPlan>()));
}

var providerPlan = await _providerPlanRepository.GetByProviderId(id);
return View(new ProviderEditModel(provider, users, providerOrganizations, providerPlan));
var providerPlans = await _providerPlanRepository.GetByProviderId(id);

return View(new ProviderEditModel(provider, users, providerOrganizations, providerPlans.ToList()));
}

[HttpPost]
Expand All @@ -177,14 +177,15 @@ public async Task<IActionResult> Edit(Guid id)
[RequirePermission(Permission.Provider_Edit)]
public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
{
var providerPlans = await _providerPlanRepository.GetByProviderId(id);
var provider = await _providerRepository.GetByIdAsync(id);

if (provider == null)
{
return RedirectToAction("Index");
}

model.ToProvider(provider);

await _providerRepository.ReplaceAsync(provider);
await _applicationCacheService.UpsertProviderAbilityAsync(provider);

Expand All @@ -195,13 +196,14 @@ public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
return RedirectToAction("Edit", new { id });
}

model.ToProviderPlan(providerPlans);
var providerPlans = await _providerPlanRepository.GetByProviderId(id);

if (providerPlans.Count == 0)
{
var newProviderPlans = new List<ProviderPlan>
{
new() {ProviderId = provider.Id, PlanType = PlanType.TeamsMonthly, SeatMinimum= model.TeamsMinimumSeats, PurchasedSeats = 0, AllocatedSeats = 0},
new() {ProviderId = provider.Id, PlanType = PlanType.EnterpriseMonthly, SeatMinimum= model.EnterpriseMinimumSeats, PurchasedSeats = 0, AllocatedSeats = 0}
new () { ProviderId = provider.Id, PlanType = PlanType.TeamsMonthly, SeatMinimum = model.TeamsMonthlySeatMinimum, PurchasedSeats = 0, AllocatedSeats = 0 },
new () { ProviderId = provider.Id, PlanType = PlanType.EnterpriseMonthly, SeatMinimum = model.EnterpriseMonthlySeatMinimum, PurchasedSeats = 0, AllocatedSeats = 0 }
};

foreach (var newProviderPlan in newProviderPlans)
Expand All @@ -213,6 +215,15 @@ public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
{
foreach (var providerPlan in providerPlans)
{
if (providerPlan.PlanType == PlanType.EnterpriseMonthly)
{
providerPlan.SeatMinimum = model.EnterpriseMonthlySeatMinimum;
}
else if (providerPlan.PlanType == PlanType.TeamsMonthly)
{
providerPlan.SeatMinimum = model.TeamsMonthlySeatMinimum;
}

await _providerPlanRepository.ReplaceAsync(providerPlan);
}
}
Expand Down
16 changes: 8 additions & 8 deletions src/Admin/AdminConsole/Models/CreateProviderModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ public CreateProviderModel() { }
[Display(Name = "Primary Billing Email")]
public string BillingEmail { get; set; }

[Display(Name = "Teams minimum seats")]
public int TeamsMinimumSeats { get; set; }
[Display(Name = "Teams (Monthly) Seat Minimum")]
public int TeamsMonthlySeatMinimum { get; set; }

[Display(Name = "Enterprise minimum seats")]
public int EnterpriseMinimumSeats { get; set; }
[Display(Name = "Enterprise (Monthly) Seat Minimum")]
public int EnterpriseMonthlySeatMinimum { get; set; }

public virtual Provider ToProvider()
{
Expand All @@ -51,14 +51,14 @@ public IEnumerable<ValidationResult> Validate(ValidationContext validationContex
var ownerEmailDisplayName = nameof(OwnerEmail).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(OwnerEmail);
yield return new ValidationResult($"The {ownerEmailDisplayName} field is required.");
}
if (TeamsMinimumSeats < 0)
if (TeamsMonthlySeatMinimum < 0)
{
var teamsMinimumSeatsDisplayName = nameof(TeamsMinimumSeats).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(TeamsMinimumSeats);
var teamsMinimumSeatsDisplayName = nameof(TeamsMonthlySeatMinimum).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(TeamsMonthlySeatMinimum);
yield return new ValidationResult($"The {teamsMinimumSeatsDisplayName} field can not be negative.");
}
if (EnterpriseMinimumSeats < 0)
if (EnterpriseMonthlySeatMinimum < 0)
{
var enterpriseMinimumSeatsDisplayName = nameof(EnterpriseMinimumSeats).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(EnterpriseMinimumSeats);
var enterpriseMinimumSeatsDisplayName = nameof(EnterpriseMonthlySeatMinimum).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(EnterpriseMonthlySeatMinimum);
yield return new ValidationResult($"The {enterpriseMinimumSeatsDisplayName} field can not be negative.");
}
break;
Expand Down
65 changes: 30 additions & 35 deletions src/Admin/AdminConsole/Models/ProviderEditModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ public class ProviderEditModel : ProviderViewModel
{
public ProviderEditModel() { }

public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers,
IEnumerable<ProviderOrganizationOrganizationDetails> organizations, IEnumerable<ProviderPlan> providerPlans)
: base(provider, providerUsers, organizations)
public ProviderEditModel(
Provider provider,
IEnumerable<ProviderUserUserDetails> providerUsers,
IEnumerable<ProviderOrganizationOrganizationDetails> organizations,
IReadOnlyCollection<ProviderPlan> providerPlans) : base(provider, providerUsers, organizations)
{
Name = provider.DisplayName();
BusinessName = provider.DisplayBusinessName();
BillingEmail = provider.BillingEmail;
BillingPhone = provider.BillingPhone;
TeamsMinimumSeats = GetMinimumSeats(providerPlans, PlanType.TeamsMonthly);
EnterpriseMinimumSeats = GetMinimumSeats(providerPlans, PlanType.EnterpriseMonthly);
TeamsMonthlySeatMinimum = GetSeatMinimum(providerPlans, PlanType.TeamsMonthly);
EnterpriseMonthlySeatMinimum = GetSeatMinimum(providerPlans, PlanType.EnterpriseMonthly);
Gateway = provider.Gateway;
GatewayCustomerId = provider.GatewayCustomerId;
GatewaySubscriptionId = provider.GatewaySubscriptionId;
}

[Display(Name = "Billing Email")]
Expand All @@ -29,38 +34,28 @@ public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails>
[Display(Name = "Business Name")]
public string BusinessName { get; set; }
public string Name { get; set; }
[Display(Name = "Teams minimum seats")]
public int TeamsMinimumSeats { get; set; }

[Display(Name = "Enterprise minimum seats")]
public int EnterpriseMinimumSeats { get; set; }
[Display(Name = "Events")]

public IEnumerable<ProviderPlan> ToProviderPlan(IEnumerable<ProviderPlan> existingProviderPlans)
{
var providerPlans = existingProviderPlans.ToList();
foreach (var existingProviderPlan in providerPlans)
{
existingProviderPlan.SeatMinimum = existingProviderPlan.PlanType switch
{
PlanType.TeamsMonthly => TeamsMinimumSeats,
PlanType.EnterpriseMonthly => EnterpriseMinimumSeats,
_ => existingProviderPlan.SeatMinimum
};
}
return providerPlans;
}

public Provider ToProvider(Provider existingProvider)
[Display(Name = "Teams (Monthly) Seat Minimum")]
public int TeamsMonthlySeatMinimum { get; set; }

[Display(Name = "Enterprise (Monthly) Seat Minimum")]
public int EnterpriseMonthlySeatMinimum { get; set; }
[Display(Name = "Gateway")]
public GatewayType? Gateway { get; set; }
[Display(Name = "Gateway Customer Id")]
public string GatewayCustomerId { get; set; }
[Display(Name = "Gateway Subscription Id")]
public string GatewaySubscriptionId { get; set; }

public virtual Provider ToProvider(Provider existingProvider)
{
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
existingProvider.BillingPhone = BillingPhone?.ToLowerInvariant()?.Trim();
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant().Trim();
existingProvider.BillingPhone = BillingPhone?.ToLowerInvariant().Trim();
existingProvider.Gateway = Gateway;
existingProvider.GatewayCustomerId = GatewayCustomerId;
existingProvider.GatewaySubscriptionId = GatewaySubscriptionId;
return existingProvider;
}


private int GetMinimumSeats(IEnumerable<ProviderPlan> providerPlans, PlanType planType)
{
return (from providerPlan in providerPlans where providerPlan.PlanType == planType select (int)providerPlan.SeatMinimum).FirstOrDefault();
}
private static int GetSeatMinimum(IEnumerable<ProviderPlan> providerPlans, PlanType planType)
=> providerPlans.FirstOrDefault(providerPlan => providerPlan.PlanType == planType)?.SeatMinimum ?? 0;
}
8 changes: 4 additions & 4 deletions src/Admin/AdminConsole/Views/Providers/Create.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@
<div class="row">
<div class="col-sm">
<div class="form-group">
<label asp-for="TeamsMinimumSeats"></label>
<input type="number" class="form-control" asp-for="TeamsMinimumSeats">
<label asp-for="TeamsMonthlySeatMinimum"></label>
<input type="number" class="form-control" asp-for="TeamsMonthlySeatMinimum">
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label asp-for="EnterpriseMinimumSeats"></label>
<input type="number" class="form-control" asp-for="EnterpriseMinimumSeats">
<label asp-for="EnterpriseMonthlySeatMinimum"></label>
<input type="number" class="form-control" asp-for="EnterpriseMonthlySeatMinimum">
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 54824b0

Please sign in to comment.