[PM-37083] feat: Add per-phase price resolution to UpdateOrganizationSubscriptionCommand#7695
Conversation
…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.
Bitwarden Claude Code ReviewOverall Assessment: APPROVE Reviewed the per-phase price resolution change to Code Review DetailsNo findings. Notes from the review:
|
|
Codecov Report❌ Patch coverage is 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. 🚀 New features to boost your workflow:
|



🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-37083
📔 Objective
Adds per-phase price resolution to
UpdateOrganizationSubscriptionCommandso 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:
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 byEndDate > now, and builds per-phase options with each phase's appropriate price IDs.EndBehavior.Releaseon schedule updates — the codebase has zeroEndBehavior.Cancelreferences, and the list query doesn't filter to scheduler-created schedules.count == 1is 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.CreateAndConfigureScheduleAsyncnow 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.Releaseis 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.updatedevent per mutation, migration coupon correctly applied only to Phase 1 discountable items.Related:
PM-37084 (#7686) will land alongside this — the
SubscriberServicecancellation/recovery lifecycle changes belong there.