Skip to content

feat: Adds admin UI for active user billing + adds a minseats option#27929

Open
sean-brydon wants to merge 3 commits intomainfrom
feat/active-user-billing-mode-minseats
Open

feat: Adds admin UI for active user billing + adds a minseats option#27929
sean-brydon wants to merge 3 commits intomainfrom
feat/active-user-billing-mode-minseats

Conversation

@sean-brydon
Copy link
Member

@sean-brydon sean-brydon commented Feb 13, 2026

How to test

  • enable active user billing feature flag
  • enable stripe on your instance
  • run yarn stripe:listen in root
  • Login as admin
  • Goto /settings/organizations/new
  • Create an organization with active user pricing
  • i.e 5 min seats -> $50 a "seat"
  • copy handover link
  • Paste it into a new tab (incoginto)
  • Go through with onboarding
  • Notice checkout hits min seats for setup
  • Then test active user billing by adding 2 members. create booking with one of them "to make them active"
  • Open stripe dashboard for the customer - time clock to 3 days before billing period to ensure invoice.upcoming is fired
  • See stripe updates to the min seats for that invoice
  • Create a new org -> test min seats 0 -> only bills for the users active for that month

Summary by cubic

Adds admin controls to select billing mode (Seats or Active Users) during org creation and introduces a minSeats floor for Active Users billing. The floor is applied across onboarding, checkout/handover, billing info, and invoice updates.

  • New Features

    • Admin UI: billing mode toggle; minSeats input for Active Users; seats hidden when not needed; price per seat required for Active Users.
    • Onboarding/handover: store/TRPC accept billingMode and minSeats; admin creating for another user always submits to generate handover with these fields.
    • Billing: persist billingMode/minSeats to OrganizationOnboarding, TeamBilling, and OrganizationBilling; webhook records them on activation; BillingPeriodService returns minSeats; ActiveUserBillingStrategy bills max(active users, minSeats); checkout floors by unique members and minSeats.
    • Tests: added coverage for minSeats and updated factories/strategy tests.
  • Migration

    • Adds OrganizationOnboarding.billingMode (default SEATS) and .minSeats; TeamBilling.minSeats; OrganizationBilling.minSeats. Run the migration; no backfill needed.

Written for commit e95645c. Summary will update on new commits.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Sean Brydon seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@github-actions github-actions bot added the ❗️ migrations contains migration files label Feb 13, 2026
);

const billingInfo = await this.deps.billingPeriodService.getBillingPeriodInfo(team.id);
const billedCount = Math.max(activeUserCount, billingInfo.minSeats ?? 0);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hit min seats if active count is below. If not set minSeats to 0 so we use activeUserCount

Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 19 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/features/ee/organizations/lib/service/onboarding/BaseOnboardingService.ts">

<violation number="1" location="packages/features/ee/organizations/lib/service/onboarding/BaseOnboardingService.ts:134">
P2: `billingMode`/`minSeats` are only saved for new onboarding records. When resuming an onboarding (`input.onboardingId`), the update payload omits these fields, so any changes to billing mode or min seats are silently dropped. Add these fields to the update payload so the resume flow persists them.</violation>
</file>

<file name="packages/features/ee/billing/service/seatBillingStrategy/ActiveUserBillingStrategy.ts">

<violation number="1" location="packages/features/ee/billing/service/seatBillingStrategy/ActiveUserBillingStrategy.ts:50">
P2: Move the `subscriptionItem` guard before the new `getBillingPeriodInfo` call to avoid an unnecessary async operation when the subscription has no items. Per the project's early-return convention, guard checks should precede any work that depends on those guards passing.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

pricePerSeat: input.pricePerSeat,
billingPeriod: input.billingPeriod,
billingMode: input.billingMode,
minSeats: input.minSeats,
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: billingMode/minSeats are only saved for new onboarding records. When resuming an onboarding (input.onboardingId), the update payload omits these fields, so any changes to billing mode or min seats are silently dropped. Add these fields to the update payload so the resume flow persists them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/features/ee/organizations/lib/service/onboarding/BaseOnboardingService.ts, line 134:

<comment>`billingMode`/`minSeats` are only saved for new onboarding records. When resuming an onboarding (`input.onboardingId`), the update payload omits these fields, so any changes to billing mode or min seats are silently dropped. Add these fields to the update payload so the resume flow persists them.</comment>

<file context>
@@ -130,6 +130,8 @@ export abstract class BaseOnboardingService implements IOrganizationOnboardingSe
       pricePerSeat: input.pricePerSeat,
       billingPeriod: input.billingPeriod,
+      billingMode: input.billingMode,
+      minSeats: input.minSeats,
       createdByUserId: this.user.id,
       logo: processedAssets.logo ?? null,
</file context>
Fix with Cubic

periodEnd
);

const billingInfo = await this.deps.billingPeriodService.getBillingPeriodInfo(team.id);
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Feb 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Move the subscriptionItem guard before the new getBillingPeriodInfo call to avoid an unnecessary async operation when the subscription has no items. Per the project's early-return convention, guard checks should precede any work that depends on those guards passing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/features/ee/billing/service/seatBillingStrategy/ActiveUserBillingStrategy.ts, line 50:

<comment>Move the `subscriptionItem` guard before the new `getBillingPeriodInfo` call to avoid an unnecessary async operation when the subscription has no items. Per the project's early-return convention, guard checks should precede any work that depends on those guards passing.</comment>

<file context>
@@ -45,6 +47,9 @@ export class ActiveUserBillingStrategy extends BaseSeatBillingStrategy {
       periodEnd
     );
 
+    const billingInfo = await this.deps.billingPeriodService.getBillingPeriodInfo(team.id);
+    const billedCount = Math.max(activeUserCount, billingInfo.minSeats ?? 0);
+
</file context>
Fix with Cubic

@github-actions
Copy link
Contributor

Devin AI is addressing Cubic AI's review feedback

A Devin session has been created to address the issues identified by Cubic AI.

View Devin Session

@sean-brydon sean-brydon marked this pull request as ready for review February 13, 2026 11:06
@sean-brydon sean-brydon requested a review from a team as a code owner February 13, 2026 11:06
@graphite-app graphite-app bot added the core area: core, team members only label Feb 13, 2026
@graphite-app graphite-app bot requested a review from a team February 13, 2026 11:07
@devin-ai-integration
Copy link
Contributor

Reviewed both Cubic AI findings. Neither meets the 9/10 confidence threshold for automated fixes:

  • Violation 1 (BaseOnboardingService.ts:134) — Confidence 7/10: billingMode/minSeats missing from update payload on resume flow. Skipping as below threshold.
  • Violation 2 (ActiveUserBillingStrategy.ts:50) — Confidence 8/10: subscriptionItem guard ordering. Skipping as below threshold.

No changes pushed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

consumer core area: core, team members only ❗️ migrations contains migration files size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants