Skip to content

[PM-37083] feat: Add per-phase price resolution to UpdateOrganizationSubscriptionCommand#7695

Merged
amorask-bitwarden merged 2 commits into
mainfrom
billing/PM-37083/per-phase-price-resolving-organization-subscription-updates
May 22, 2026
Merged

[PM-37083] feat: Add per-phase price resolution to UpdateOrganizationSubscriptionCommand#7695
amorask-bitwarden merged 2 commits into
mainfrom
billing/PM-37083/per-phase-price-resolving-organization-subscription-updates

Conversation

@amorask-bitwarden
Copy link
Copy Markdown
Contributor

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-37083

📔 Objective

Adds per-phase price resolution to UpdateOrganizationSubscriptionCommand so item mutations against a migration-attached subscription update each phase using the correct phase-specific price IDs. Also resolves a schedule-normalization defect where direct subscription metadata writes were causing Stripe to expand the schedule into 3 phases.

Per-phase price resolution:

  • Adds OrganizationPlanMigrationPriceMapper — maps source-plan price IDs to target-plan equivalents per slot (PM seat, SM seat, storage, SA), with pass-through fallback for uniform slots.
  • UpdateOrganizationSubscriptionCommand's schedule-aware path now resolves source/target plans via cohort lookup, filters phases by EndDate > now, and builds per-phase options with each phase's appropriate price IDs.
  • Hard-codes EndBehavior.Release on schedule updates — the codebase has zero EndBehavior.Cancel references, and the list query doesn't filter to scheduler-created schedules.
  • Defensive single-phase guard: count == 1 is only treated as post-migration if the phase's items actually use target-plan price IDs, protecting against legacy source-priced single-phase schedules wrongly having their migration discount cleared.

Schedule-normalization defect fix:

  • PriceIncreaseScheduler.CreateAndConfigureScheduleAsync now stamps cohort metadata on each phase rather than relying on a follow-up subscription update. Stripe propagates phase metadata to subscription metadata automatically (empirically verified) without triggering normalization.
  • PriceIncreaseScheduler.Release is no longer gated by feature flags — schedule existence is the gate.

Verification:

End-to-end verified against Stripe — PM seat expansion (mapped), storage add/remove (uniform), SM seat add (uniform), SA add (mapped). Schedule stays at 2 phases across mutations, single subscription_schedule.updated event per mutation, migration coupon correctly applied only to Phase 1 discountable items.

Related:

PM-37084 (#7686) will land alongside this — the SubscriberService cancellation/recovery lifecycle changes belong there.

…SubscriptionCommand

Resolve source vs. target plan pricing per schedule phase so item changes
target the correct phase-specific price ID. Move cohort metadata onto the
schedule phases themselves to avoid Stripe normalization triggered by
direct subscription metadata updates. Filter the schedule-aware update
path to phases where EndDate > now, and drop the feature-flag gate on
PriceIncreaseScheduler.Release so schedule existence is the gate.
@amorask-bitwarden amorask-bitwarden added the ai-review Request a Claude code review label May 22, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the per-phase price resolution change to UpdateOrganizationSubscriptionCommand and the related schedule-normalization fix in PriceIncreaseScheduler. The implementation correctly resolves source/target plans via cohort lookup, filters phases by EndDate > now, and applies phase-specific price translation through the new OrganizationPlanMigrationPriceMapper. The defensive IsPostMigrationPhase guard for legacy single-phase schedules is well-reasoned and explicitly tested. Test coverage is thorough across multiple-phase scenarios, metadata preservation, and migration vs non-migration paths.

Code Review Details

No findings.

Notes from the review:

  • Verified EndBehavior.Cancel is absent from the codebase, supporting the hardcoded Release choice.
  • Confirmed PriceIncreaseScheduler is the only schedule creator, so removing the feature-flag gate from Release is safe — schedule existence is sufficient.
  • The IsPostMigrationPhase check uses target-priced item detection rather than relying solely on phase count, which correctly protects against misclassifying legacy source-priced single-phase schedules.
  • MapOrPassThrough short-circuits via ReferenceEquals when no migration is in effect, preserving prior behavior for non-migrated organizations.

@sonarqubecloud
Copy link
Copy Markdown

@amorask-bitwarden amorask-bitwarden marked this pull request as ready for review May 22, 2026 16:00
@amorask-bitwarden amorask-bitwarden requested a review from a team as a code owner May 22, 2026 16:00
@amorask-bitwarden amorask-bitwarden requested a review from kdenney May 22, 2026 16:00
@codecov
Copy link
Copy Markdown

codecov Bot commented May 22, 2026

Codecov Report

❌ Patch coverage is 93.63057% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.49%. Comparing base (d903096) to head (f898848).

Files with missing lines Patch % Lines
.../Commands/UpdateOrganizationSubscriptionCommand.cs 95.28% 2 Missing and 3 partials ⚠️
src/Core/Billing/Pricing/PriceIncreaseScheduler.cs 83.33% 4 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #7695      +/-   ##
==========================================
- Coverage   64.86%   60.49%   -4.38%     
==========================================
  Files        2140     2141       +1     
  Lines       94629    94654      +25     
  Branches     8445     8457      +12     
==========================================
- Hits        61378    57257    -4121     
- Misses      31155    35392    +4237     
+ Partials     2096     2005      -91     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@amorask-bitwarden amorask-bitwarden merged commit b1395aa into main May 22, 2026
44 checks passed
@amorask-bitwarden amorask-bitwarden deleted the billing/PM-37083/per-phase-price-resolving-organization-subscription-updates branch May 22, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants