mirror of
https://github.com/bitwarden/server.git
synced 2024-11-22 12:15:36 +01:00
[AC-2262] As a Bitwarden Admin, I need a ways to set and update an MSP's minimum seats (#3956)
* initial commit Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * add the feature flag Signed-off-by: Cy Okeke <cokeke@bitwarden.com> * Add featureflag for create and edit html pages Signed-off-by: Cy Okeke <cokeke@bitwarden.com> --------- Signed-off-by: Cy Okeke <cokeke@bitwarden.com>
This commit is contained in:
parent
108d22f484
commit
5bd2c424aa
@ -1,10 +1,15 @@
|
|||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core;
|
||||||
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Enums.Provider;
|
using Bit.Core.AdminConsole.Enums.Provider;
|
||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
|
using Bit.Core.Billing.Entities;
|
||||||
|
using Bit.Core.Billing.Repositories;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
|
||||||
namespace Bit.Commercial.Core.AdminConsole.Providers;
|
namespace Bit.Commercial.Core.AdminConsole.Providers;
|
||||||
|
|
||||||
@ -14,21 +19,28 @@ public class CreateProviderCommand : ICreateProviderCommand
|
|||||||
private readonly IProviderUserRepository _providerUserRepository;
|
private readonly IProviderUserRepository _providerUserRepository;
|
||||||
private readonly IProviderService _providerService;
|
private readonly IProviderService _providerService;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IProviderPlanRepository _providerPlanRepository;
|
||||||
|
private readonly IFeatureService _featureService;
|
||||||
|
|
||||||
public CreateProviderCommand(
|
public CreateProviderCommand(
|
||||||
IProviderRepository providerRepository,
|
IProviderRepository providerRepository,
|
||||||
IProviderUserRepository providerUserRepository,
|
IProviderUserRepository providerUserRepository,
|
||||||
IProviderService providerService,
|
IProviderService providerService,
|
||||||
IUserRepository userRepository)
|
IUserRepository userRepository,
|
||||||
|
IProviderPlanRepository providerPlanRepository,
|
||||||
|
IFeatureService featureService)
|
||||||
{
|
{
|
||||||
_providerRepository = providerRepository;
|
_providerRepository = providerRepository;
|
||||||
_providerUserRepository = providerUserRepository;
|
_providerUserRepository = providerUserRepository;
|
||||||
_providerService = providerService;
|
_providerService = providerService;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
|
_providerPlanRepository = providerPlanRepository;
|
||||||
|
_featureService = featureService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateMspAsync(Provider provider, string ownerEmail)
|
public async Task CreateMspAsync(Provider provider, string ownerEmail, int teamsMinimumSeats, int enterpriseMinimumSeats)
|
||||||
{
|
{
|
||||||
|
var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
|
||||||
var owner = await _userRepository.GetByEmailAsync(ownerEmail);
|
var owner = await _userRepository.GetByEmailAsync(ownerEmail);
|
||||||
if (owner == null)
|
if (owner == null)
|
||||||
{
|
{
|
||||||
@ -44,8 +56,24 @@ public class CreateProviderCommand : ICreateProviderCommand
|
|||||||
Type = ProviderUserType.ProviderAdmin,
|
Type = ProviderUserType.ProviderAdmin,
|
||||||
Status = ProviderUserStatusType.Confirmed,
|
Status = ProviderUserStatusType.Confirmed,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (isConsolidatedBillingEnabled)
|
||||||
|
{
|
||||||
|
var providerPlans = new List<ProviderPlan>
|
||||||
|
{
|
||||||
|
CreateProviderPlan(provider.Id, PlanType.TeamsMonthly, teamsMinimumSeats),
|
||||||
|
CreateProviderPlan(provider.Id, PlanType.EnterpriseMonthly, enterpriseMinimumSeats)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var providerPlan in providerPlans)
|
||||||
|
{
|
||||||
|
await _providerPlanRepository.CreateAsync(providerPlan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await _providerUserRepository.CreateAsync(providerUser);
|
await _providerUserRepository.CreateAsync(providerUser);
|
||||||
await _providerService.SendProviderSetupInviteEmailAsync(provider, owner.Email);
|
await _providerService.SendProviderSetupInviteEmailAsync(provider, owner.Email);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CreateResellerAsync(Provider provider)
|
public async Task CreateResellerAsync(Provider provider)
|
||||||
@ -60,4 +88,16 @@ public class CreateProviderCommand : ICreateProviderCommand
|
|||||||
provider.UseEvents = true;
|
provider.UseEvents = true;
|
||||||
await _providerRepository.CreateAsync(provider);
|
await _providerRepository.CreateAsync(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ProviderPlan CreateProviderPlan(Guid providerId, PlanType planType, int seatMinimum)
|
||||||
|
{
|
||||||
|
return new ProviderPlan
|
||||||
|
{
|
||||||
|
ProviderId = providerId,
|
||||||
|
PlanType = planType,
|
||||||
|
SeatMinimum = seatMinimum,
|
||||||
|
PurchasedSeats = 0,
|
||||||
|
AllocatedSeats = 0
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ public class CreateProviderCommandTests
|
|||||||
provider.Type = ProviderType.Msp;
|
provider.Type = ProviderType.Msp;
|
||||||
|
|
||||||
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
var exception = await Assert.ThrowsAsync<BadRequestException>(
|
||||||
() => sutProvider.Sut.CreateMspAsync(provider, default));
|
() => sutProvider.Sut.CreateMspAsync(provider, default, default, default));
|
||||||
Assert.Contains("Invalid owner.", exception.Message);
|
Assert.Contains("Invalid owner.", exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ public class CreateProviderCommandTests
|
|||||||
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
var userRepository = sutProvider.GetDependency<IUserRepository>();
|
||||||
userRepository.GetByEmailAsync(user.Email).Returns(user);
|
userRepository.GetByEmailAsync(user.Email).Returns(user);
|
||||||
|
|
||||||
await sutProvider.Sut.CreateMspAsync(provider, user.Email);
|
await sutProvider.Sut.CreateMspAsync(provider, user.Email, default, default);
|
||||||
|
|
||||||
await sutProvider.GetDependency<IProviderRepository>().ReceivedWithAnyArgs().CreateAsync(default);
|
await sutProvider.GetDependency<IProviderRepository>().ReceivedWithAnyArgs().CreateAsync(default);
|
||||||
await sutProvider.GetDependency<IProviderService>().Received(1).SendProviderSetupInviteEmailAsync(provider, user.Email);
|
await sutProvider.GetDependency<IProviderService>().Received(1).SendProviderSetupInviteEmailAsync(provider, user.Email);
|
||||||
|
@ -8,6 +8,8 @@ using Bit.Core.AdminConsole.Enums.Provider;
|
|||||||
using Bit.Core.AdminConsole.Providers.Interfaces;
|
using Bit.Core.AdminConsole.Providers.Interfaces;
|
||||||
using Bit.Core.AdminConsole.Repositories;
|
using Bit.Core.AdminConsole.Repositories;
|
||||||
using Bit.Core.AdminConsole.Services;
|
using Bit.Core.AdminConsole.Services;
|
||||||
|
using Bit.Core.Billing.Entities;
|
||||||
|
using Bit.Core.Billing.Repositories;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
@ -34,6 +36,7 @@ public class ProvidersController : Controller
|
|||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly ICreateProviderCommand _createProviderCommand;
|
private readonly ICreateProviderCommand _createProviderCommand;
|
||||||
private readonly IFeatureService _featureService;
|
private readonly IFeatureService _featureService;
|
||||||
|
private readonly IProviderPlanRepository _providerPlanRepository;
|
||||||
|
|
||||||
public ProvidersController(
|
public ProvidersController(
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
@ -47,7 +50,8 @@ public class ProvidersController : Controller
|
|||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
IUserService userService,
|
IUserService userService,
|
||||||
ICreateProviderCommand createProviderCommand,
|
ICreateProviderCommand createProviderCommand,
|
||||||
IFeatureService featureService)
|
IFeatureService featureService,
|
||||||
|
IProviderPlanRepository providerPlanRepository)
|
||||||
{
|
{
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
@ -61,6 +65,7 @@ public class ProvidersController : Controller
|
|||||||
_userService = userService;
|
_userService = userService;
|
||||||
_createProviderCommand = createProviderCommand;
|
_createProviderCommand = createProviderCommand;
|
||||||
_featureService = featureService;
|
_featureService = featureService;
|
||||||
|
_providerPlanRepository = providerPlanRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[RequirePermission(Permission.Provider_List_View)]
|
[RequirePermission(Permission.Provider_List_View)]
|
||||||
@ -90,11 +95,13 @@ public class ProvidersController : Controller
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public IActionResult Create(string ownerEmail = null)
|
public IActionResult Create(int teamsMinimumSeats, int enterpriseMinimumSeats, string ownerEmail = null)
|
||||||
{
|
{
|
||||||
return View(new CreateProviderModel
|
return View(new CreateProviderModel
|
||||||
{
|
{
|
||||||
OwnerEmail = ownerEmail
|
OwnerEmail = ownerEmail,
|
||||||
|
TeamsMinimumSeats = teamsMinimumSeats,
|
||||||
|
EnterpriseMinimumSeats = enterpriseMinimumSeats
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +119,8 @@ public class ProvidersController : Controller
|
|||||||
switch (provider.Type)
|
switch (provider.Type)
|
||||||
{
|
{
|
||||||
case ProviderType.Msp:
|
case ProviderType.Msp:
|
||||||
await _createProviderCommand.CreateMspAsync(provider, model.OwnerEmail);
|
await _createProviderCommand.CreateMspAsync(provider, model.OwnerEmail, model.TeamsMinimumSeats,
|
||||||
|
model.EnterpriseMinimumSeats);
|
||||||
break;
|
break;
|
||||||
case ProviderType.Reseller:
|
case ProviderType.Reseller:
|
||||||
await _createProviderCommand.CreateResellerAsync(provider);
|
await _createProviderCommand.CreateResellerAsync(provider);
|
||||||
@ -139,6 +147,7 @@ public class ProvidersController : Controller
|
|||||||
[SelfHosted(NotSelfHostedOnly = true)]
|
[SelfHosted(NotSelfHostedOnly = true)]
|
||||||
public async Task<IActionResult> Edit(Guid id)
|
public async Task<IActionResult> Edit(Guid id)
|
||||||
{
|
{
|
||||||
|
var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
|
||||||
var provider = await _providerRepository.GetByIdAsync(id);
|
var provider = await _providerRepository.GetByIdAsync(id);
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
@ -147,7 +156,12 @@ public class ProvidersController : Controller
|
|||||||
|
|
||||||
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
var users = await _providerUserRepository.GetManyDetailsByProviderAsync(id);
|
||||||
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
var providerOrganizations = await _providerOrganizationRepository.GetManyDetailsByProviderAsync(id);
|
||||||
return View(new ProviderEditModel(provider, users, providerOrganizations));
|
if (isConsolidatedBillingEnabled)
|
||||||
|
{
|
||||||
|
var providerPlan = await _providerPlanRepository.GetByProviderId(id);
|
||||||
|
return View(new ProviderEditModel(provider, users, providerOrganizations, providerPlan));
|
||||||
|
}
|
||||||
|
return View(new ProviderEditModel(provider, users, providerOrganizations, new List<ProviderPlan>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
@ -156,6 +170,8 @@ public class ProvidersController : Controller
|
|||||||
[RequirePermission(Permission.Provider_Edit)]
|
[RequirePermission(Permission.Provider_Edit)]
|
||||||
public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
|
public async Task<IActionResult> Edit(Guid id, ProviderEditModel model)
|
||||||
{
|
{
|
||||||
|
var isConsolidatedBillingEnabled = _featureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling);
|
||||||
|
var providerPlans = await _providerPlanRepository.GetByProviderId(id);
|
||||||
var provider = await _providerRepository.GetByIdAsync(id);
|
var provider = await _providerRepository.GetByIdAsync(id);
|
||||||
if (provider == null)
|
if (provider == null)
|
||||||
{
|
{
|
||||||
@ -165,6 +181,15 @@ public class ProvidersController : Controller
|
|||||||
model.ToProvider(provider);
|
model.ToProvider(provider);
|
||||||
await _providerRepository.ReplaceAsync(provider);
|
await _providerRepository.ReplaceAsync(provider);
|
||||||
await _applicationCacheService.UpsertProviderAbilityAsync(provider);
|
await _applicationCacheService.UpsertProviderAbilityAsync(provider);
|
||||||
|
if (isConsolidatedBillingEnabled)
|
||||||
|
{
|
||||||
|
model.ToProviderPlan(providerPlans);
|
||||||
|
foreach (var providerPlan in providerPlans)
|
||||||
|
{
|
||||||
|
await _providerPlanRepository.ReplaceAsync(providerPlan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return RedirectToAction("Edit", new { id });
|
return RedirectToAction("Edit", new { id });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,12 @@ public class CreateProviderModel : IValidatableObject
|
|||||||
[Display(Name = "Primary Billing Email")]
|
[Display(Name = "Primary Billing Email")]
|
||||||
public string BillingEmail { get; set; }
|
public string BillingEmail { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Teams minimum seats")]
|
||||||
|
public int TeamsMinimumSeats { get; set; }
|
||||||
|
|
||||||
|
[Display(Name = "Enterprise minimum seats")]
|
||||||
|
public int EnterpriseMinimumSeats { get; set; }
|
||||||
|
|
||||||
public virtual Provider ToProvider()
|
public virtual Provider ToProvider()
|
||||||
{
|
{
|
||||||
return new Provider()
|
return new Provider()
|
||||||
@ -45,6 +51,16 @@ public class CreateProviderModel : IValidatableObject
|
|||||||
var ownerEmailDisplayName = nameof(OwnerEmail).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(OwnerEmail);
|
var ownerEmailDisplayName = nameof(OwnerEmail).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(OwnerEmail);
|
||||||
yield return new ValidationResult($"The {ownerEmailDisplayName} field is required.");
|
yield return new ValidationResult($"The {ownerEmailDisplayName} field is required.");
|
||||||
}
|
}
|
||||||
|
if (TeamsMinimumSeats < 0)
|
||||||
|
{
|
||||||
|
var teamsMinimumSeatsDisplayName = nameof(TeamsMinimumSeats).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(TeamsMinimumSeats);
|
||||||
|
yield return new ValidationResult($"The {teamsMinimumSeatsDisplayName} field can not be negative.");
|
||||||
|
}
|
||||||
|
if (EnterpriseMinimumSeats < 0)
|
||||||
|
{
|
||||||
|
var enterpriseMinimumSeatsDisplayName = nameof(EnterpriseMinimumSeats).GetDisplayAttribute<CreateProviderModel>()?.GetName() ?? nameof(EnterpriseMinimumSeats);
|
||||||
|
yield return new ValidationResult($"The {enterpriseMinimumSeatsDisplayName} field can not be negative.");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ProviderType.Reseller:
|
case ProviderType.Reseller:
|
||||||
if (string.IsNullOrWhiteSpace(Name))
|
if (string.IsNullOrWhiteSpace(Name))
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using Bit.Core.AdminConsole.Entities.Provider;
|
using Bit.Core.AdminConsole.Entities.Provider;
|
||||||
using Bit.Core.AdminConsole.Models.Data.Provider;
|
using Bit.Core.AdminConsole.Models.Data.Provider;
|
||||||
|
using Bit.Core.Billing.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Admin.AdminConsole.Models;
|
namespace Bit.Admin.AdminConsole.Models;
|
||||||
|
|
||||||
@ -8,13 +10,16 @@ public class ProviderEditModel : ProviderViewModel
|
|||||||
{
|
{
|
||||||
public ProviderEditModel() { }
|
public ProviderEditModel() { }
|
||||||
|
|
||||||
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers, IEnumerable<ProviderOrganizationOrganizationDetails> organizations)
|
public ProviderEditModel(Provider provider, IEnumerable<ProviderUserUserDetails> providerUsers,
|
||||||
|
IEnumerable<ProviderOrganizationOrganizationDetails> organizations, IEnumerable<ProviderPlan> providerPlans)
|
||||||
: base(provider, providerUsers, organizations)
|
: base(provider, providerUsers, organizations)
|
||||||
{
|
{
|
||||||
Name = provider.DisplayName();
|
Name = provider.DisplayName();
|
||||||
BusinessName = provider.DisplayBusinessName();
|
BusinessName = provider.DisplayBusinessName();
|
||||||
BillingEmail = provider.BillingEmail;
|
BillingEmail = provider.BillingEmail;
|
||||||
BillingPhone = provider.BillingPhone;
|
BillingPhone = provider.BillingPhone;
|
||||||
|
TeamsMinimumSeats = GetMinimumSeats(providerPlans, PlanType.TeamsMonthly);
|
||||||
|
EnterpriseMinimumSeats = GetMinimumSeats(providerPlans, PlanType.EnterpriseMonthly);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Display(Name = "Billing Email")]
|
[Display(Name = "Billing Email")]
|
||||||
@ -24,12 +29,38 @@ public class ProviderEditModel : ProviderViewModel
|
|||||||
[Display(Name = "Business Name")]
|
[Display(Name = "Business Name")]
|
||||||
public string BusinessName { get; set; }
|
public string BusinessName { get; set; }
|
||||||
public string Name { 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")]
|
[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)
|
public Provider ToProvider(Provider existingProvider)
|
||||||
{
|
{
|
||||||
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
existingProvider.BillingEmail = BillingEmail?.ToLowerInvariant()?.Trim();
|
||||||
existingProvider.BillingPhone = BillingPhone?.ToLowerInvariant()?.Trim();
|
existingProvider.BillingPhone = BillingPhone?.ToLowerInvariant()?.Trim();
|
||||||
return existingProvider;
|
return existingProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int GetMinimumSeats(IEnumerable<ProviderPlan> providerPlans, PlanType planType)
|
||||||
|
{
|
||||||
|
return (from providerPlan in providerPlans where providerPlan.PlanType == planType select (int)providerPlan.SeatMinimum).FirstOrDefault();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
@using Bit.SharedWeb.Utilities
|
@using Bit.SharedWeb.Utilities
|
||||||
@using Bit.Core.AdminConsole.Enums.Provider
|
@using Bit.Core.AdminConsole.Enums.Provider
|
||||||
|
@using Bit.Core
|
||||||
@model CreateProviderModel
|
@model CreateProviderModel
|
||||||
|
@inject Bit.Core.Services.IFeatureService FeatureService
|
||||||
@{
|
@{
|
||||||
ViewData["Title"] = "Create Provider";
|
ViewData["Title"] = "Create Provider";
|
||||||
}
|
}
|
||||||
@ -39,6 +41,23 @@
|
|||||||
<label asp-for="OwnerEmail"></label>
|
<label asp-for="OwnerEmail"></label>
|
||||||
<input type="text" class="form-control" asp-for="OwnerEmail">
|
<input type="text" class="form-control" asp-for="OwnerEmail">
|
||||||
</div>
|
</div>
|
||||||
|
@if (FeatureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling))
|
||||||
|
{
|
||||||
|
<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">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="EnterpriseMinimumSeats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="EnterpriseMinimumSeats">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="@($"info-{(int)ProviderType.Reseller}")" class="form-group @(Model.Type != ProviderType.Reseller ? "d-none" : string.Empty)">
|
<div id="@($"info-{(int)ProviderType.Reseller}")" class="form-group @(Model.Type != ProviderType.Reseller ? "d-none" : string.Empty)">
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
@using Bit.Admin.Enums;
|
@using Bit.Admin.Enums;
|
||||||
|
@using Bit.Core
|
||||||
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
@inject Bit.Admin.Services.IAccessControlService AccessControlService
|
||||||
|
@inject Bit.Core.Services.IFeatureService FeatureService
|
||||||
|
|
||||||
@model ProviderEditModel
|
@model ProviderEditModel
|
||||||
@{
|
@{
|
||||||
@ -41,6 +43,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@if (FeatureService.IsEnabled(FeatureFlagKeys.EnableConsolidatedBilling))
|
||||||
|
{
|
||||||
|
<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">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm">
|
||||||
|
<div class="form-group">
|
||||||
|
<label asp-for="EnterpriseMinimumSeats"></label>
|
||||||
|
<input type="number" class="form-control" asp-for="EnterpriseMinimumSeats">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</form>
|
</form>
|
||||||
@await Html.PartialAsync("Organizations", Model)
|
@await Html.PartialAsync("Organizations", Model)
|
||||||
@if (canEdit)
|
@if (canEdit)
|
||||||
|
@ -4,6 +4,6 @@ namespace Bit.Core.AdminConsole.Providers.Interfaces;
|
|||||||
|
|
||||||
public interface ICreateProviderCommand
|
public interface ICreateProviderCommand
|
||||||
{
|
{
|
||||||
Task CreateMspAsync(Provider provider, string ownerEmail);
|
Task CreateMspAsync(Provider provider, string ownerEmail, int teamsMinimumSeats, int enterpriseMinimumSeats);
|
||||||
Task CreateResellerAsync(Provider provider);
|
Task CreateResellerAsync(Provider provider);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user