Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public class PreviewOrganizationTaxCommand(
ISubscriptionDiscountService subscriptionDiscountService)
: BaseBillingCommand<PreviewOrganizationTaxCommand>(logger), IPreviewOrganizationTaxCommand
{
private readonly ILogger<PreviewOrganizationTaxCommand> _logger = logger;

public Task<BillingCommandResult<(decimal Tax, decimal Total)>> Run(
User user,
OrganizationSubscriptionPurchase purchase,
Expand Down Expand Up @@ -231,15 +233,34 @@ public class PreviewOrganizationTaxCommand(

var items = new List<InvoiceSubscriptionDetailsItemOptions>();

var passwordManagerSeats = subscriptionItemsByPriceId[
currentPlan.HasNonSeatBasedPasswordManagerPlan()
long quantity;

if (currentPlan.HasNonSeatBasedPasswordManagerPlan() && !newPlan.HasNonSeatBasedPasswordManagerPlan())
{
// The current plan doesn't have a per-seat subscription item to read a quantity from
// (e.g. upgrading from a flat-rate plan like Teams Starter), so fall back to the
// organization's occupied seat count instead of looking it up on the subscription.
quantity = (long)organization.Seats!;
}
else
{
var passwordManagerPriceId = currentPlan.HasNonSeatBasedPasswordManagerPlan()
? currentPlan.PasswordManager.StripePlanId
: currentPlan.PasswordManager.StripeSeatPlanId];
: currentPlan.PasswordManager.StripeSeatPlanId;

var quantity = currentPlan.HasNonSeatBasedPasswordManagerPlan() &&
!newPlan.HasNonSeatBasedPasswordManagerPlan()
? (long)organization.Seats!
: passwordManagerSeats.Quantity;
if (!subscriptionItemsByPriceId.TryGetValue(passwordManagerPriceId, out var passwordManagerSeats))
{
_logger.LogError(
"Organization {OrganizationId}'s subscription {SubscriptionId} does not contain a " +
"Password Manager line item matching its current plan's price {PriceId}",
organization.Id, organization.GatewaySubscriptionId, passwordManagerPriceId);

return new BadRequest(
"Your organization's subscription does not match its current plan. Please contact support for assistance.");
}

quantity = passwordManagerSeats.Quantity;
}
Comment on lines +238 to +263

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ QUESTION: This bugfix (and the new BadRequest path) has no accompanying unit test.

Details

The existing Subscription Plan Change tests in PreviewOrganizationTaxCommandTests.cs only exercise current plans of Free, Families, Teams, and Enterprise β€” none of which are non-seat-based (HasNonSeatBasedPasswordManagerPlan()). The Teams Starter 2023 β†’ seat-based upgrade path introduced here, and the new missing-line-item BadRequest return, are both untested.

Per the repo convention (.claude/CLAUDE.md: "ALWAYS add unit tests"), consider adding:

  1. A regression test upgrading from a non-seat-based plan (e.g. PlanType.TeamsStarter2023) that asserts quantity == organization.Seats.
  2. A test where the current plan's price is absent from subscription.Items, asserting the BadRequest result.

This locks in the fix and prevents regression of the original crash.


items.Add(new InvoiceSubscriptionDetailsItemOptions
{
Expand Down
Loading