1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 21:18:13 -05:00

[PM-15814]Alert owners of reseller-managed orgs to renewal events (#5193)

* Changes for the admin console alert

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Fix the failing test

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

* Add the feature flag

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>

---------

Signed-off-by: Cy Okeke <cokeke@bitwarden.com>
This commit is contained in:
cyprain-okeke 2024-12-31 18:06:29 +01:00 committed by GitHub
parent 83404efebd
commit d924c6721a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 37 additions and 6 deletions

View File

@ -7,7 +7,11 @@ public record OrganizationMetadataResponse(
bool IsManaged, bool IsManaged,
bool IsOnSecretsManagerStandalone, bool IsOnSecretsManagerStandalone,
bool IsSubscriptionUnpaid, bool IsSubscriptionUnpaid,
bool HasSubscription) bool HasSubscription,
bool HasOpenInvoice,
DateTime? InvoiceDueDate,
DateTime? InvoiceCreatedDate,
DateTime? SubPeriodEndDate)
{ {
public static OrganizationMetadataResponse From(OrganizationMetadata metadata) public static OrganizationMetadataResponse From(OrganizationMetadata metadata)
=> new( => new(
@ -15,5 +19,9 @@ public record OrganizationMetadataResponse(
metadata.IsManaged, metadata.IsManaged,
metadata.IsOnSecretsManagerStandalone, metadata.IsOnSecretsManagerStandalone,
metadata.IsSubscriptionUnpaid, metadata.IsSubscriptionUnpaid,
metadata.HasSubscription); metadata.HasSubscription,
metadata.HasOpenInvoice,
metadata.InvoiceDueDate,
metadata.InvoiceCreatedDate,
metadata.SubPeriodEndDate);
} }

View File

@ -5,4 +5,8 @@ public record OrganizationMetadata(
bool IsManaged, bool IsManaged,
bool IsOnSecretsManagerStandalone, bool IsOnSecretsManagerStandalone,
bool IsSubscriptionUnpaid, bool IsSubscriptionUnpaid,
bool HasSubscription); bool HasSubscription,
bool HasOpenInvoice,
DateTime? InvoiceDueDate,
DateTime? InvoiceCreatedDate,
DateTime? SubPeriodEndDate);

View File

@ -68,19 +68,25 @@ public class OrganizationBillingService(
if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId)) if (string.IsNullOrWhiteSpace(organization.GatewaySubscriptionId))
{ {
return new OrganizationMetadata(isEligibleForSelfHost, isManaged, false, return new OrganizationMetadata(isEligibleForSelfHost, isManaged, false,
false, false); false, false, false, null, null, null);
} }
var customer = await subscriberService.GetCustomer(organization, var customer = await subscriberService.GetCustomer(organization,
new CustomerGetOptions { Expand = ["discount.coupon.applies_to"] }); new CustomerGetOptions { Expand = ["discount.coupon.applies_to"] });
var subscription = await subscriberService.GetSubscription(organization); var subscription = await subscriberService.GetSubscription(organization);
var isOnSecretsManagerStandalone = IsOnSecretsManagerStandalone(organization, customer, subscription); var isOnSecretsManagerStandalone = IsOnSecretsManagerStandalone(organization, customer, subscription);
var isSubscriptionUnpaid = IsSubscriptionUnpaid(subscription); var isSubscriptionUnpaid = IsSubscriptionUnpaid(subscription);
var hasSubscription = true; var hasSubscription = true;
var openInvoice = await HasOpenInvoiceAsync(subscription);
var hasOpenInvoice = openInvoice.HasOpenInvoice;
var invoiceDueDate = openInvoice.DueDate;
var invoiceCreatedDate = openInvoice.CreatedDate;
var subPeriodEndDate = subscription?.CurrentPeriodEnd;
return new OrganizationMetadata(isEligibleForSelfHost, isManaged, isOnSecretsManagerStandalone, return new OrganizationMetadata(isEligibleForSelfHost, isManaged, isOnSecretsManagerStandalone,
isSubscriptionUnpaid, hasSubscription); isSubscriptionUnpaid, hasSubscription, hasOpenInvoice, invoiceDueDate, invoiceCreatedDate, subPeriodEndDate);
} }
public async Task UpdatePaymentMethod( public async Task UpdatePaymentMethod(
@ -393,6 +399,18 @@ public class OrganizationBillingService(
return subscription.Status == "unpaid"; return subscription.Status == "unpaid";
} }
private async Task<(bool HasOpenInvoice, DateTime? CreatedDate, DateTime? DueDate)> HasOpenInvoiceAsync(Subscription subscription)
{
if (subscription?.LatestInvoiceId == null)
{
return (false, null, null);
}
var invoice = await stripeAdapter.InvoiceGetAsync(subscription.LatestInvoiceId, new InvoiceGetOptions());
return invoice?.Status == "open"
? (true, invoice.Created, invoice.DueDate)
: (false, null, null);
}
#endregion #endregion
} }

View File

@ -163,6 +163,7 @@ public static class FeatureFlagKeys
public const string AuthenticatorSynciOS = "enable-authenticator-sync-ios"; public const string AuthenticatorSynciOS = "enable-authenticator-sync-ios";
public const string AuthenticatorSyncAndroid = "enable-authenticator-sync-android"; public const string AuthenticatorSyncAndroid = "enable-authenticator-sync-android";
public const string AppReviewPrompt = "app-review-prompt"; public const string AppReviewPrompt = "app-review-prompt";
public const string ResellerManagedOrgAlert = "PM-15814-alert-owners-of-reseller-managed-orgs";
public static List<string> GetAllKeys() public static List<string> GetAllKeys()
{ {

View File

@ -52,7 +52,7 @@ public class OrganizationBillingControllerTests
{ {
sutProvider.GetDependency<ICurrentContext>().OrganizationUser(organizationId).Returns(true); sutProvider.GetDependency<ICurrentContext>().OrganizationUser(organizationId).Returns(true);
sutProvider.GetDependency<IOrganizationBillingService>().GetMetadata(organizationId) sutProvider.GetDependency<IOrganizationBillingService>().GetMetadata(organizationId)
.Returns(new OrganizationMetadata(true, true, true, true, true)); .Returns(new OrganizationMetadata(true, true, true, true, true, true, null, null, null));
var result = await sutProvider.Sut.GetMetadataAsync(organizationId); var result = await sutProvider.Sut.GetMetadataAsync(organizationId);