mirror of
https://github.com/bitwarden/server.git
synced 2026-05-31 16:17:04 -05:00
* feat(billing): add Organization.ChangePlan extension for structural plan shape Pure helper that writes plan-derived structural columns (PlanType, Plan, Use* capability flags, UsersGetPremium, MaxCollections) without touching customer-purchase columns. Preserves the existing UseKeyConnector carve-out. Behavior-preserving extraction of the field-copy block at lines 287-310 of UpgradeOrganizationPlanCommand.UpgradePlanAsync. * refactor(billing): use Organization.ChangePlan in UpgradePlanAsync Replaces the inline structural field-copy block with a call to the new ChangePlan helper. Customer-purchase columns (Seats, MaxStorageGb, Enabled, UseSecretsManager) and the PremiumAccessAddon override on UsersGetPremium stay inline at the call site. Behavior-preserving. * feat(billing): register Teams 2020 -> current migration paths Appends Teams2020AnnualToCurrent (byte 3) and Teams2020MonthlyToCurrent (byte 4) to MigrationPathId and the MigrationPaths registry. Required for the business migration handler to resolve cohorts mapped to Teams source plans; without these entries MigrationPaths.FromId returns null and the handler no-ops on Teams orgs in Cohort A1. Snapshot tests updated. * chore(billing): inject migration cohort repositories into SubscriptionUpdatedHandler Adds IOrganizationPlanMigrationCohortRepository and IOrganizationPlanMigrationCohortAssignmentRepository as constructor dependencies in preparation for HandleScheduleTriggeredBusinessMigrationAsync. No behavior change. * feat(billing): scaffold business plan Phase-2 migration handler Adds HandleScheduleTriggeredBusinessMigrationAsync as a sibling call in HandleAsync's organization branch, gated on PM35215_BusinessPlanPriceMigration. Initial implementation short-circuits when ScheduleId is null. Locks the no-op behaviour with NoScheduleId + FeatureFlagOff tests. Full handler body lands in subsequent commits. * feat(billing): gate business migration handler on registered source price IDs Builds the source-price allowlist from MigrationPaths.All, using the seat-vs-non-seat pattern (HasNonSeatBasedPasswordManagerPlan). All four Track A 2020 plans register automatically. Skips when the previous subscription items don't include any registered 2020 source price. * feat(billing): resolve cohort via assignment row for business migration handler Reads assignment by organization id (DB is source of truth), then resolves the cohort and migration path. Stripe subscription.Metadata['migration_cohort_id'] remains stamped by PriceIncreaseScheduler for dashboard attribution but is not consulted by the handler. Skips with a warning when the assignment is missing, the cohort is missing, or MigrationPathId references an unregistered path. Idempotent: skips with info-level log when assignment.MigratedDate is already set, before any further DB reads. * feat(billing): defensive target-price sanity check for business migration After resolving the target plan from cohort.MigrationPath.ToPlan, verifies the current subscription items contain the target's PM price ID (seat-aware). Skips with a warning on mismatch to protect against operator data errors or off-path schedule transitions. * feat(billing): apply plan shape and mark assignment migrated on Phase 2 Completes HandleScheduleTriggeredBusinessMigrationAsync: loads the org, calls Organization.ChangePlan(targetPlan), persists via ReplaceAsync, and sets assignment.MigratedDate + RevisionDate before persisting the assignment. Happy-path coverage for all four Track A pairs (Teams + Enterprise, monthly + annual). Teams tests assert the UseScim flip - the load-bearing capability gain for Teams 2020 -> current. * Add more unit test * fix: add UTF-8 BOM to .cs files for editorconfig charset compliance * Add exception handle * Code refactoring * Add more unit testing * Resolve the pr comment