[PM-37514] Support Teams 2019 Migration#7864
Conversation
…on logic comments
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #7864 +/- ##
==========================================
+ Coverage 61.21% 61.22% +0.01%
==========================================
Files 2217 2219 +2
Lines 98051 98098 +47
Branches 8849 8853 +4
==========================================
+ Hits 60023 60065 +42
- Misses 35911 35916 +5
Partials 2117 2117 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
🤖 Bitwarden Claude Code ReviewOverall Assessment: APPROVE Reviewed the Teams 2019 → Teams Current migration paths added to the deferred price-increase engine. Examined the new Code Review DetailsNo blocking findings. The change is well-scoped and backed by thorough unit tests covering the seat-resolution boundaries (below base, at base, above base, purchased > occupied), storage line-item preservation, sub-5 source detection by base price, and the snapshot/registry immutability guards. |



🎟️ Tracking
https://bitwarden.atlassian.net/browse/PM-37514
📔 Objective
Adds the Teams 2019 → Teams Current pricing-migration paths (Monthly and Annual) to the deferred
price-increase migration engine (epic PM-37496).
"Teams 2019 Packaged" is a flat base covering 5 included seats, plus a per-seat overage for seats beyond 5. Current Teams is pure per-seat.
disappears.
Unlike Teams Starter (a flat bundle), Teams 2019 carries a per-seat overage line, so
HasNonSeatBasedPasswordManagerPlan()is false for it — that distinction drives most of thechanges below.
Code changes
Teams2019AnnualToCurrent/Teams2019MonthlyToCurrent(append-only
MigrationPathIds 9, 10; 7–8 reserved for the in-flight Teams Starter paths). AddsSeatCountPolicy { Preserve, ActualUsage }toMigrationPath; Teams 2019 usesActualUsage, allother paths default to
Preserve.OrganizationPlanMigrationPriceMapper) — a guarded Packaged → Scalable casemaps the flat base price (
StripePlanId) to the target per-seat price; the not-null guard keeps anull == nullmisfire off Scalable sources.PriceIncreaseScheduler) — forActualUsage, the base line and theseat-overage line (both map to the same target seat price) collapse into a single seat line
billed at
occupied < base ? occupied : purchased, floored at 1. Storage and other items passthrough unchanged.
UpcomingInvoiceHandler) — reusesBusinessPlanRenewal2020MigrationMail; thequoted seat count matches what Phase 2 bills (occupied for < 5, purchased for ≥ 5).
SubscriptionUpdatedHandler) — source detection now identifies packagedsources by their base price (so a sub-5 Teams 2019 org, which has no overage line, is still
recognized when the schedule advances), and the
organization.Seatsreconciliation (PM-39562,[PM-39562] fix: Reconcile org seats to billed quantity on packaged-plan migration #7869) now also fires for Teams 2019 — reconciling
Seatsto the billed quantity.Notes for reviewers
MaxAutoscaleSeatsis preserved by construction —ChangePlandoesn't touch it and thereconciliation writes only
Seats.📸 Screenshots
Incoming