From b49c16f529dac20a11ead2db0f0da1240e5052b2 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Tue, 11 Jul 2017 10:59:59 -0400 Subject: [PATCH] storage adjustment and billing fixes --- src/Api/Controllers/AccountsController.cs | 2 +- .../Controllers/OrganizationsController.cs | 13 +++++++++ .../Request/Accounts/StorageRequestModel.cs | 6 ++-- src/Core/Services/IOrganizationService.cs | 1 + .../Implementations/OrganizationService.cs | 29 +++++++++++++++++-- src/Core/Utilities/BillingHelpers.cs | 14 ++++----- 6 files changed, 51 insertions(+), 14 deletions(-) diff --git a/src/Api/Controllers/AccountsController.cs b/src/Api/Controllers/AccountsController.cs index b16e5ea222..e54be78ab4 100644 --- a/src/Api/Controllers/AccountsController.cs +++ b/src/Api/Controllers/AccountsController.cs @@ -395,7 +395,7 @@ namespace Bit.Api.Controllers throw new UnauthorizedAccessException(); } - await _userService.AdjustStorageAsync(user, model.StroageGbAdjustment.Value); + await _userService.AdjustStorageAsync(user, model.StorageGbAdjustment.Value); } [HttpPut("cancel-premium")] diff --git a/src/Api/Controllers/OrganizationsController.cs b/src/Api/Controllers/OrganizationsController.cs index 2cdbaf0a91..368b6471b4 100644 --- a/src/Api/Controllers/OrganizationsController.cs +++ b/src/Api/Controllers/OrganizationsController.cs @@ -168,6 +168,19 @@ namespace Bit.Api.Controllers await _organizationService.AdjustSeatsAsync(orgIdGuid, model.SeatAdjustment.Value); } + [HttpPut("{id}/storage")] + [HttpPost("{id}/storage")] + public async Task PutStorage(string id, [FromBody]StorageRequestModel model) + { + var orgIdGuid = new Guid(id); + if(!_currentContext.OrganizationOwner(orgIdGuid)) + { + throw new NotFoundException(); + } + + await _organizationService.AdjustStorageAsync(orgIdGuid, model.StorageGbAdjustment.Value); + } + [HttpPut("{id}/cancel")] [HttpPost("{id}/cancel")] public async Task PutCancel(string id) diff --git a/src/Core/Models/Api/Request/Accounts/StorageRequestModel.cs b/src/Core/Models/Api/Request/Accounts/StorageRequestModel.cs index 054d9d0fb6..5d72f7d9e4 100644 --- a/src/Core/Models/Api/Request/Accounts/StorageRequestModel.cs +++ b/src/Core/Models/Api/Request/Accounts/StorageRequestModel.cs @@ -6,14 +6,14 @@ namespace Bit.Core.Models.Api public class StorageRequestModel : IValidatableObject { [Required] - public short? StroageGbAdjustment { get; set; } + public short? StorageGbAdjustment { get; set; } public IEnumerable Validate(ValidationContext validationContext) { - if(StroageGbAdjustment == 0) + if(StorageGbAdjustment == 0) { yield return new ValidationResult("Storage adjustment cannot be 0.", - new string[] { nameof(StroageGbAdjustment) }); + new string[] { nameof(StorageGbAdjustment) }); } } } diff --git a/src/Core/Services/IOrganizationService.cs b/src/Core/Services/IOrganizationService.cs index 3ad9f84bd8..cd560cae59 100644 --- a/src/Core/Services/IOrganizationService.cs +++ b/src/Core/Services/IOrganizationService.cs @@ -14,6 +14,7 @@ namespace Bit.Core.Services Task CancelSubscriptionAsync(Guid organizationId, bool endOfPeriod = false); Task ReinstateSubscriptionAsync(Guid organizationId); Task UpgradePlanAsync(Guid organizationId, PlanType plan, int additionalSeats); + Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb); Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment); Task> SignUpAsync(OrganizationSignup organizationSignup); Task DeleteAsync(Organization organization); diff --git a/src/Core/Services/Implementations/OrganizationService.cs b/src/Core/Services/Implementations/OrganizationService.cs index c94d69a080..8041ef5a70 100644 --- a/src/Core/Services/Implementations/OrganizationService.cs +++ b/src/Core/Services/Implementations/OrganizationService.cs @@ -222,6 +222,29 @@ namespace Bit.Core.Services // TODO: Update organization } + public async Task AdjustStorageAsync(Guid organizationId, short storageAdjustmentGb) + { + var organization = await _organizationRepository.GetByIdAsync(organizationId); + if(organization == null) + { + throw new NotFoundException(); + } + + var plan = StaticStore.Plans.FirstOrDefault(p => p.Type == organization.PlanType); + if(plan == null) + { + throw new BadRequestException("Existing plan not found."); + } + + if(!plan.MaxStorageGb.HasValue) + { + throw new BadRequestException("Plan does not allow additional storage."); + } + + await BillingHelpers.AdjustStorageAsync(organization, storageAdjustmentGb, plan.StripStoragePlanId); + await _organizationRepository.ReplaceAsync(organization); + } + public async Task AdjustSeatsAsync(Guid organizationId, int seatAdjustment) { var organization = await _organizationRepository.GetByIdAsync(organizationId); @@ -288,7 +311,7 @@ namespace Bit.Core.Services } var seatItem = sub.Items?.Data?.FirstOrDefault(i => i.Plan.Id == plan.StripeSeatPlanId); - if(seatItem == null) + if(additionalSeats > 0 && seatItem == null) { await subscriptionItemService.CreateAsync(new StripeSubscriptionItemCreateOptions { @@ -298,7 +321,7 @@ namespace Bit.Core.Services SubscriptionId = sub.Id }); } - else if(additionalSeats > 0) + else if(additionalSeats > 0 && seatItem != null) { await subscriptionItemService.UpdateAsync(seatItem.Id, new StripeSubscriptionItemUpdateOptions { @@ -307,7 +330,7 @@ namespace Bit.Core.Services Prorate = true }); } - else if(additionalSeats == 0) + else if(seatItem != null && additionalSeats == 0) { await subscriptionItemService.DeleteAsync(seatItem.Id); } diff --git a/src/Core/Utilities/BillingHelpers.cs b/src/Core/Utilities/BillingHelpers.cs index 912f531e44..59d4dbaf70 100644 --- a/src/Core/Utilities/BillingHelpers.cs +++ b/src/Core/Utilities/BillingHelpers.cs @@ -183,8 +183,8 @@ namespace Bit.Core.Utilities throw new BadRequestException("Subscription not found."); } - var seatItem = sub.Items?.Data?.FirstOrDefault(i => i.Plan.Id == storagePlanId); - if(seatItem == null) + var storageItem = sub.Items?.Data?.FirstOrDefault(i => i.Plan.Id == storagePlanId); + if(additionalStorage > 0 && storageItem == null) { await subscriptionItemService.CreateAsync(new StripeSubscriptionItemCreateOptions { @@ -194,23 +194,23 @@ namespace Bit.Core.Utilities SubscriptionId = sub.Id }); } - else if(additionalStorage > 0) + else if(additionalStorage > 0 && storageItem != null) { - await subscriptionItemService.UpdateAsync(seatItem.Id, new StripeSubscriptionItemUpdateOptions + await subscriptionItemService.UpdateAsync(storageItem.Id, new StripeSubscriptionItemUpdateOptions { PlanId = storagePlanId, Quantity = additionalStorage, Prorate = true }); } - else if(additionalStorage == 0) + else if(additionalStorage == 0 && storageItem != null) { - await subscriptionItemService.DeleteAsync(storagePlanId); + await subscriptionItemService.DeleteAsync(storageItem.Id); } if(additionalStorage > 0) { - await PreviewUpcomingInvoiceAndPayAsync(storableSubscriber, storagePlanId, 300); + await PreviewUpcomingInvoiceAndPayAsync(storableSubscriber, storagePlanId, 400); } storableSubscriber.MaxStorageGb = newStorageGb;