From 5705f4f736db55737267af924a7a768c594d7001 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 10 Apr 2017 10:44:27 -0400 Subject: [PATCH] separate plans for month vs annual. users => seats --- .../OrganizationCreateRequestModel.cs | 8 +- .../Api/Response/OrganizationResponseModel.cs | 4 +- .../Models/Business/OrganizationChangePlan.cs | 13 --- .../Models/Business/OrganizationSignup.cs | 3 +- src/Core/Models/StaticStore/Plan.cs | 10 +-- src/Core/Models/Table/Organization.cs | 2 +- .../Implementations/OrganizationService.cs | 90 ++++++++++--------- src/Core/Utilities/StaticStore.cs | 30 +++---- src/Sql/Sql.sqlproj | 3 + .../Stored Procedures/Organization_Create.sql | 6 +- .../Stored Procedures/Organization_Update.sql | 4 +- src/Sql/dbo/Tables/Organization.sql | 2 +- 12 files changed, 82 insertions(+), 93 deletions(-) delete mode 100644 src/Core/Models/Business/OrganizationChangePlan.cs diff --git a/src/Core/Models/Api/Request/Organizations/OrganizationCreateRequestModel.cs b/src/Core/Models/Api/Request/Organizations/OrganizationCreateRequestModel.cs index 0cf3487b5c..0b40d2426c 100644 --- a/src/Core/Models/Api/Request/Organizations/OrganizationCreateRequestModel.cs +++ b/src/Core/Models/Api/Request/Organizations/OrganizationCreateRequestModel.cs @@ -21,8 +21,7 @@ namespace Bit.Core.Models.Api public string Key { get; set; } public string PaymentToken { get; set; } [Range(0, double.MaxValue)] - public short AdditionalUsers { get; set; } - public bool Monthly { get; set; } + public short AdditionalSeats { get; set; } public virtual OrganizationSignup ToOrganizationSignup(User user) { @@ -33,10 +32,9 @@ namespace Bit.Core.Models.Api Name = Name, Plan = PlanType, PaymentToken = PaymentToken, - AdditionalUsers = AdditionalUsers, + AdditionalSeats = AdditionalSeats, BillingEmail = BillingEmail, - BusinessName = BusinessName, - Monthly = Monthly + BusinessName = BusinessName }; } diff --git a/src/Core/Models/Api/Response/OrganizationResponseModel.cs b/src/Core/Models/Api/Response/OrganizationResponseModel.cs index 801303bd16..ebeaee21b0 100644 --- a/src/Core/Models/Api/Response/OrganizationResponseModel.cs +++ b/src/Core/Models/Api/Response/OrganizationResponseModel.cs @@ -23,7 +23,7 @@ namespace Bit.Core.Models.Api BillingEmail = organization.BillingEmail; Plan = organization.Plan; PlanType = organization.PlanType; - MaxUsers = organization.MaxUsers; + Seats = organization.Seats; } public string Id { get; set; } @@ -32,7 +32,7 @@ namespace Bit.Core.Models.Api public string BillingEmail { get; set; } public string Plan { get; set; } public Enums.PlanType PlanType { get; set; } - public short? MaxUsers { get; set; } + public short? Seats { get; set; } } public class OrganizationBillingResponseModel : OrganizationResponseModel diff --git a/src/Core/Models/Business/OrganizationChangePlan.cs b/src/Core/Models/Business/OrganizationChangePlan.cs deleted file mode 100644 index 5d80b8cf29..0000000000 --- a/src/Core/Models/Business/OrganizationChangePlan.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Bit.Core.Enums; -using System; - -namespace Bit.Core.Models.Business -{ - public class OrganizationChangePlan - { - public Guid OrganizationId { get; set; } - public PlanType PlanType { get; set; } - public short AdditionalUsers { get; set; } - public bool Monthly { get; set; } - } -} diff --git a/src/Core/Models/Business/OrganizationSignup.cs b/src/Core/Models/Business/OrganizationSignup.cs index 1842bf1a7b..4a65f61829 100644 --- a/src/Core/Models/Business/OrganizationSignup.cs +++ b/src/Core/Models/Business/OrganizationSignup.cs @@ -11,8 +11,7 @@ namespace Bit.Core.Models.Business public User Owner { get; set; } public string OwnerKey { get; set; } public Enums.PlanType Plan { get; set; } - public short AdditionalUsers { get; set; } + public short AdditionalSeats { get; set; } public string PaymentToken { get; set; } - public bool Monthly { get; set; } } } diff --git a/src/Core/Models/StaticStore/Plan.cs b/src/Core/Models/StaticStore/Plan.cs index 8f9c83968c..08e5bc7bce 100644 --- a/src/Core/Models/StaticStore/Plan.cs +++ b/src/Core/Models/StaticStore/Plan.cs @@ -6,13 +6,13 @@ namespace Bit.Core.Models.StaticStore { public string Name { get; set; } public string StripePlanId { get; set; } - public string StripeUserPlanId { get; set; } + public string StripeSeatPlanId { get; set; } public PlanType Type { get; set; } - public short BaseUsers { get; set; } - public bool CanBuyAdditionalUsers { get; set; } - public short? MaxAdditionalUsers { get; set; } + public short BaseSeats { get; set; } + public bool CanBuyAdditionalSeats { get; set; } + public short? MaxAdditionalSeats { get; set; } public decimal BasePrice { get; set; } - public decimal UserPrice { get; set; } + public decimal SeatPrice { get; set; } public short? MaxSubvaults { get; set; } public int UpgradeSortOrder { get; set; } public bool Disabled { get; set; } diff --git a/src/Core/Models/Table/Organization.cs b/src/Core/Models/Table/Organization.cs index 94c5f2bfbb..abf78dc463 100644 --- a/src/Core/Models/Table/Organization.cs +++ b/src/Core/Models/Table/Organization.cs @@ -12,7 +12,7 @@ namespace Bit.Core.Models.Table public string BillingEmail { get; set; } public string Plan { get; set; } public PlanType PlanType { get; set; } - public short? MaxUsers { get; set; } + public short? Seats { get; set; } public short? MaxSubvaults { get; set; } public string StripeCustomerId { get; set; } public string StripeSubscriptionId { get; set; } diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index d72c8ad37c..30204bcafc 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -9,6 +9,7 @@ using Bit.Core.Exceptions; using System.Collections.Generic; using Microsoft.AspNetCore.DataProtection; using Stripe; +using Bit.Core.Enums; namespace Bit.Core.Services { @@ -160,9 +161,9 @@ namespace Bit.Core.Services } } - public async Task UpgradePlanAsync(OrganizationChangePlan model) + public async Task UpgradePlanAsync(Guid organizationId, PlanType plan, short additionalSeats) { - var organization = await _organizationRepository.GetByIdAsync(model.OrganizationId); + var organization = await _organizationRepository.GetByIdAsync(organizationId); if(organization == null) { throw new NotFoundException(); @@ -179,7 +180,7 @@ namespace Bit.Core.Services throw new BadRequestException("Existing plan not found."); } - var newPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == model.PlanType && !p.Disabled); + var newPlan = StaticStore.Plans.FirstOrDefault(p => p.Type == plan && !p.Disabled); if(newPlan == null) { throw new BadRequestException("Plan not found."); @@ -195,26 +196,26 @@ namespace Bit.Core.Services throw new BadRequestException("You cannot upgrade to this plan."); } - if(!newPlan.CanBuyAdditionalUsers && model.AdditionalUsers > 0) + if(!newPlan.CanBuyAdditionalSeats && additionalSeats > 0) { - throw new BadRequestException("Plan does not allow additional users."); + throw new BadRequestException("Plan does not allow additional seats."); } - if(newPlan.CanBuyAdditionalUsers && newPlan.MaxAdditionalUsers.HasValue && - model.AdditionalUsers > newPlan.MaxAdditionalUsers.Value) + if(newPlan.CanBuyAdditionalSeats && newPlan.MaxAdditionalSeats.HasValue && + additionalSeats > newPlan.MaxAdditionalSeats.Value) { throw new BadRequestException($"Selected plan allows a maximum of " + - $"{newPlan.MaxAdditionalUsers.Value} additional users."); + $"{newPlan.MaxAdditionalSeats.Value} additional seats."); } - var newPlanMaxUsers = (short)(newPlan.BaseUsers + (newPlan.CanBuyAdditionalUsers ? model.AdditionalUsers : 0)); - if(!organization.MaxUsers.HasValue || organization.MaxUsers.Value > newPlanMaxUsers) + var newPlanSeats = (short)(newPlan.BaseSeats + (newPlan.CanBuyAdditionalSeats ? additionalSeats : 0)); + if(!organization.Seats.HasValue || organization.Seats.Value > newPlanSeats) { var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); - if(userCount >= newPlanMaxUsers) + if(userCount >= newPlanSeats) { - throw new BadRequestException($"Your organization currently has {userCount} users. Your new plan " + - $"allows for a maximum of ({newPlanMaxUsers}) users. Remove some users."); + throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " + + $"only has ({newPlanSeats}) seats. Remove some users."); } } @@ -225,7 +226,8 @@ namespace Bit.Core.Services if(subvaultCount > newPlan.MaxSubvaults.Value) { throw new BadRequestException($"Your organization currently has {subvaultCount} subvaults. " + - $"Your new plan allows for a maximum of ({newPlan.MaxSubvaults.Value}) users. Remove some subvaults."); + $"Your new plan allows for a maximum of ({newPlan.MaxSubvaults.Value}) subvaults. " + + "Remove some subvaults."); } } @@ -245,12 +247,12 @@ namespace Bit.Core.Services } }; - if(model.AdditionalUsers > 0) + if(additionalSeats > 0) { subCreateOptions.Items.Add(new StripeSubscriptionItemOption { - PlanId = newPlan.StripeUserPlanId, - Quantity = model.AdditionalUsers + PlanId = newPlan.StripeSeatPlanId, + Quantity = additionalSeats }); } @@ -271,12 +273,12 @@ namespace Bit.Core.Services } }; - if(model.AdditionalUsers > 0) + if(additionalSeats > 0) { subUpdateOptions.Items.Add(new StripeSubscriptionItemUpdateOption { - PlanId = newPlan.StripeUserPlanId, - Quantity = model.AdditionalUsers + PlanId = newPlan.StripeSeatPlanId, + Quantity = additionalSeats }); } @@ -284,7 +286,7 @@ namespace Bit.Core.Services } } - public async Task AdjustAdditionalUsersAsync(Guid organizationId, short additionalUsers) + public async Task AdjustAdditionalSeatsAsync(Guid organizationId, short additionalSeats) { var organization = await _organizationRepository.GetByIdAsync(organizationId); if(organization == null) @@ -308,25 +310,25 @@ namespace Bit.Core.Services throw new BadRequestException("Existing plan not found."); } - if(!plan.CanBuyAdditionalUsers) + if(!plan.CanBuyAdditionalSeats) { - throw new BadRequestException("Plan does not allow additional users."); + throw new BadRequestException("Plan does not allow additional seats."); } - if(plan.MaxAdditionalUsers.HasValue && additionalUsers > plan.MaxAdditionalUsers.Value) + if(plan.MaxAdditionalSeats.HasValue && additionalSeats > plan.MaxAdditionalSeats.Value) { throw new BadRequestException($"Organization plan allows a maximum of " + - $"{plan.MaxAdditionalUsers.Value} additional users."); + $"{plan.MaxAdditionalSeats.Value} additional seats."); } - var planNewMaxUsers = (short)(plan.BaseUsers + additionalUsers); - if(!organization.MaxUsers.HasValue || organization.MaxUsers.Value > planNewMaxUsers) + var planNewSeats = (short)(plan.BaseSeats + additionalSeats); + if(!organization.Seats.HasValue || organization.Seats.Value > planNewSeats) { var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organization.Id); - if(userCount >= planNewMaxUsers) + if(userCount >= planNewSeats) { - throw new BadRequestException($"Your organization currently has {userCount} users. Your new plan " + - $"allows for a maximum of ({planNewMaxUsers}) users. Remove some users."); + throw new BadRequestException($"Your organization currently has {userCount} seats filled. Your new plan " + + $"only has ({planNewSeats}) seats. Remove some users."); } } @@ -343,12 +345,12 @@ namespace Bit.Core.Services } }; - if(additionalUsers > 0) + if(additionalSeats > 0) { subUpdateOptions.Items.Add(new StripeSubscriptionItemUpdateOption { - PlanId = plan.StripeUserPlanId, - Quantity = additionalUsers + PlanId = plan.StripeSeatPlanId, + Quantity = additionalSeats }); } @@ -368,16 +370,16 @@ namespace Bit.Core.Services StripeCustomer customer = null; StripeSubscription subscription = null; - if(!plan.CanBuyAdditionalUsers && signup.AdditionalUsers > 0) + if(!plan.CanBuyAdditionalSeats && signup.AdditionalSeats > 0) { throw new BadRequestException("Plan does not allow additional users."); } - if(plan.CanBuyAdditionalUsers && plan.MaxAdditionalUsers.HasValue && - signup.AdditionalUsers > plan.MaxAdditionalUsers.Value) + if(plan.CanBuyAdditionalSeats && plan.MaxAdditionalSeats.HasValue && + signup.AdditionalSeats > plan.MaxAdditionalSeats.Value) { throw new BadRequestException($"Selected plan allows a maximum of " + - $"{plan.MaxAdditionalUsers.GetValueOrDefault(0)} additional users."); + $"{plan.MaxAdditionalSeats.GetValueOrDefault(0)} additional users."); } if(plan.Type == Enums.PlanType.Free) @@ -410,12 +412,12 @@ namespace Bit.Core.Services } }; - if(signup.AdditionalUsers > 0) + if(signup.AdditionalSeats > 0) { subCreateOptions.Items.Add(new StripeSubscriptionItemOption { - PlanId = plan.StripeUserPlanId, - Quantity = signup.AdditionalUsers + PlanId = plan.StripeSeatPlanId, + Quantity = signup.AdditionalSeats }); } @@ -428,7 +430,7 @@ namespace Bit.Core.Services BillingEmail = signup.BillingEmail, BusinessName = signup.BusinessName, PlanType = plan.Type, - MaxUsers = (short)(plan.BaseUsers + signup.AdditionalUsers), + Seats = (short)(plan.BaseSeats + signup.AdditionalSeats), MaxSubvaults = plan.MaxSubvaults, Plan = plan.Name, StripeCustomerId = customer?.Id, @@ -484,13 +486,13 @@ namespace Bit.Core.Services throw new NotFoundException(); } - if(organization.MaxUsers.HasValue) + if(organization.Seats.HasValue) { var userCount = await _organizationUserRepository.GetCountByOrganizationIdAsync(organizationId); - if(userCount >= organization.MaxUsers.Value) + if(userCount >= organization.Seats.Value) { throw new BadRequestException("You have reached the maximum number of users " + - $"({organization.MaxUsers.Value}) for this organization."); + $"({organization.Seats.Value}) for this organization."); } } diff --git a/src/Core/Utilities/StaticStore.cs b/src/Core/Utilities/StaticStore.cs index becfe8d8a3..e9bb54e252 100644 --- a/src/Core/Utilities/StaticStore.cs +++ b/src/Core/Utilities/StaticStore.cs @@ -94,8 +94,8 @@ namespace Bit.Core.Utilities new Plan { Type = PlanType.Free, - BaseUsers = 2, - CanBuyAdditionalUsers = false, + BaseSeats = 2, + CanBuyAdditionalSeats = false, MaxSubvaults = 2, Name = "Free", UpgradeSortOrder = -1 // Always the lowest plan, cannot be upgraded to @@ -103,38 +103,38 @@ namespace Bit.Core.Utilities new Plan { Type = PlanType.PersonalAnnually, - BaseUsers = 5, + BaseSeats = 5, BasePrice = 12, - UserPrice = 12, - CanBuyAdditionalUsers = true, - MaxAdditionalUsers = 5, + SeatPrice = 12, + CanBuyAdditionalSeats = true, + MaxAdditionalSeats = 5, Name = "Personal", StripePlanId = "personal-annual", - StripeUserPlanId = "personal-user-annual", + StripeSeatPlanId = "personal-user-annual", UpgradeSortOrder = 1 }, new Plan { Type = PlanType.TeamsMonthly, - BaseUsers = 5, + BaseSeats = 5, BasePrice = 8, - UserPrice = 2.5M, - CanBuyAdditionalUsers = true, + SeatPrice = 2.5M, + CanBuyAdditionalSeats = true, Name = "Teams (Monthly)", StripePlanId = "teams-monthly", - StripeUserPlanId = "teams-user-monthly", + StripeSeatPlanId = "teams-user-monthly", UpgradeSortOrder = 2 }, new Plan { Type = PlanType.TeamsAnnually, - BaseUsers = 5, + BaseSeats = 5, BasePrice = 60, - UserPrice = 24, - CanBuyAdditionalUsers = true, + SeatPrice = 24, + CanBuyAdditionalSeats = true, Name = "Teams (Annually)", StripePlanId = "teams-annual", - StripeUserPlanId = "teams-user-annual", + StripeSeatPlanId = "teams-user-annual", UpgradeSortOrder = 2 } }; diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 5719e1dbc7..b427ba4c48 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -184,4 +184,7 @@ + + + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Organization_Create.sql b/src/Sql/dbo/Stored Procedures/Organization_Create.sql index bc1e3fc985..49cdb26f85 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Create.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Create.sql @@ -5,7 +5,7 @@ @BillingEmail NVARCHAR(50), @Plan NVARCHAR(20), @PlanType TINYINT, - @MaxUsers SMALLINT, + @Seats SMALLINT, @MaxSubvaults SMALLINT, @StripeCustomerId VARCHAR(50), @StripeSubscriptionId VARCHAR(50), @@ -23,7 +23,7 @@ BEGIN [BillingEmail], [Plan], [PlanType], - [MaxUsers], + [Seats], [MaxSubvaults], [StripeCustomerId], [StripeSubscriptionId], @@ -38,7 +38,7 @@ BEGIN @BillingEmail, @Plan, @PlanType, - @MaxUsers, + @Seats, @MaxSubvaults, @StripeCustomerId, @StripeSubscriptionId, diff --git a/src/Sql/dbo/Stored Procedures/Organization_Update.sql b/src/Sql/dbo/Stored Procedures/Organization_Update.sql index b5b4d16d13..2d248aaffe 100644 --- a/src/Sql/dbo/Stored Procedures/Organization_Update.sql +++ b/src/Sql/dbo/Stored Procedures/Organization_Update.sql @@ -5,7 +5,7 @@ @BillingEmail NVARCHAR(50), @Plan NVARCHAR(20), @PlanType TINYINT, - @MaxUsers SMALLINT, + @Seats SMALLINT, @MaxSubvaults SMALLINT, @StripeCustomerId VARCHAR(50), @StripeSubscriptionId VARCHAR(50), @@ -24,7 +24,7 @@ BEGIN [BillingEmail] = @BillingEmail, [Plan] = @Plan, [PlanType] = @PlanType, - [MaxUsers] = @MaxUsers, + [Seats] = @Seats, [MaxSubvaults] = @MaxSubvaults, [StripeCustomerId] = @StripeCustomerId, [StripeSubscriptionId] = @StripeSubscriptionId, diff --git a/src/Sql/dbo/Tables/Organization.sql b/src/Sql/dbo/Tables/Organization.sql index f4b998b65e..8f7df324cc 100644 --- a/src/Sql/dbo/Tables/Organization.sql +++ b/src/Sql/dbo/Tables/Organization.sql @@ -5,7 +5,7 @@ [BillingEmail] NVARCHAR (50) NOT NULL, [Plan] NVARCHAR (20) NOT NULL, [PlanType] TINYINT NOT NULL, - [MaxUsers] SMALLINT NULL, + [Seats] SMALLINT NULL, [MaxSubvaults] SMALLINT NULL, [StripeCustomerId] VARCHAR (50) NULL, [StripeSubscriptionId] VARCHAR (50) NULL,