Skip to content

[PM-37514] Support Teams 2019 Migration#7864

Draft
sbrown-livefront wants to merge 14 commits into
mainfrom
billing/pm-37514-support-teams-2019-migration
Draft

[PM-37514] Support Teams 2019 Migration#7864
sbrown-livefront wants to merge 14 commits into
mainfrom
billing/pm-37514-support-teams-2019-migration

Conversation

@sbrown-livefront

@sbrown-livefront sbrown-livefront commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

🎟️ 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.

  • Orgs with < 5 occupied seats are billed for actual occupied seats — unused base headroom
    disappears.
  • Orgs with ≥ 5 keep their current purchased seat count.

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 the
changes below.

Code changes

  • Paths & policy — registers Teams2019AnnualToCurrent / Teams2019MonthlyToCurrent
    (append-only MigrationPathIds 9, 10; 7–8 reserved for the in-flight Teams Starter paths). Adds
    SeatCountPolicy { Preserve, ActualUsage } to MigrationPath; Teams 2019 uses ActualUsage, all
    other paths default to Preserve.
  • Price mapping (OrganizationPlanMigrationPriceMapper) — a guarded Packaged → Scalable case
    maps the flat base price (StripePlanId) to the target per-seat price; the not-null guard keeps a
    null == null misfire off Scalable sources.
  • Phase 2 schedule (PriceIncreaseScheduler) — for ActualUsage, the base line and the
    seat-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 pass
    through unchanged.
  • Renewal email (UpcomingInvoiceHandler) — reuses BusinessPlanRenewal2020MigrationMail; the
    quoted seat count matches what Phase 2 bills (occupied for < 5, purchased for ≥ 5).
  • Renewal activation (SubscriptionUpdatedHandler) — source detection now identifies packaged
    sources 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.Seats reconciliation (PM-39562,
    [PM-39562] fix: Reconcile org seats to billed quantity on packaged-plan migration #7869) now also fires for Teams 2019 — reconciling Seats to the billed quantity.

Notes for reviewers

  • MaxAutoscaleSeats is preserved by construction — ChangePlan doesn't touch it and the
    reconciliation writes only Seats.

📸 Screenshots

Incoming

@sbrown-livefront sbrown-livefront self-assigned this Jun 24, 2026
@sbrown-livefront sbrown-livefront changed the title Billing/pm 37514 support teams 2019 migration [PM-37514] Support Teams 2019 Migration Jun 24, 2026
@sbrown-livefront sbrown-livefront added the t:feature Change Type - Feature Development label Jun 24, 2026
@codecov

codecov Bot commented Jun 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.75362% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.22%. Comparing base (0ec0b23) to head (4d8a784).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
...Services/Implementations/UpcomingInvoiceHandler.cs 80.00% 0 Missing and 3 partials ⚠️
...ices/Implementations/SubscriptionUpdatedHandler.cs 88.88% 0 Missing and 1 partial ⚠️
src/Core/Billing/Pricing/PriceIncreaseScheduler.cs 96.00% 0 Missing and 1 partial ⚠️
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.
📢 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.

@sonarqubecloud

Copy link
Copy Markdown

@sbrown-livefront sbrown-livefront added the ai-review Request a Claude code review label Jun 26, 2026
@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the Teams 2019 → Teams Current migration paths added to the deferred price-increase engine. Examined the new SeatCountPolicy value object, the scheduler's Packaged-source seat collapsing (PriceIncreaseScheduler.CalculateTargetPlanSeatCountAsync), the renewal-email seat resolution (UpcomingInvoiceHandler.ResolveSeatCountAsync), and the renewal-activation seat reconciliation (SubscriptionUpdatedHandler). The three billed-quantity touchpoints (scheduler, email, reconciliation) resolve seats consistently (occupied < base ? occupied : purchased) and the append-only MigrationPathId snapshot guard is updated correctly.

Code Review Details

No 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.

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 t:feature Change Type - Feature Development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant