feat: monthly proration service and seat tracking#26986
feat: monthly proration service and seat tracking#26986sean-brydon wants to merge 8 commits intomainfrom
Conversation
395d549 to
7bca3da
Compare
There was a problem hiding this comment.
4 issues found across 111 files
Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed.
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/billing/api/webhook/_customer.subscription.updated.ts">
<violation number="1" location="packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts:88">
P2: Select only the fields you need from TeamBilling to avoid pulling the full row.
(Based on your team's feedback about selecting only required fields for Prisma queries.) [FEEDBACK_USED]</violation>
<violation number="2" location="packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts:130">
P1: Rule violated: **Avoid Logging Sensitive Information**
Avoid logging sensitive billing identifiers (subscription/customer IDs) in webhook logs. This violates the rule against logging sensitive information.</violation>
</file>
<file name="packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts:420">
P2: Seat change logging now runs unconditionally, so seat logs are created even when the monthly-proration flag is off. This changes behavior despite the feature being gated; consider guarding these logSeatAddition calls with BillingPeriodService.shouldApplyMonthlyProration (or an equivalent flag check) before writing seat logs.</violation>
</file>
<file name="packages/trpc/server/routers/viewer/organizations/utils.ts">
<violation number="1" location="packages/trpc/server/routers/viewer/organizations/utils.ts:128">
P2: This nested filter makes seat counting O(T * N) for bulk invites. Consider precomputing counts once to keep the hot path O(N + T).</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts
Show resolved
Hide resolved
| if (createdUsers.length > 0) { | ||
| const seatTracker = new SeatChangeTrackingService(); | ||
| const trackingTeamId = parentId ?? teamId; | ||
| await seatTracker.logSeatAddition({ |
There was a problem hiding this comment.
P2: Seat change logging now runs unconditionally, so seat logs are created even when the monthly-proration flag is off. This changes behavior despite the feature being gated; consider guarding these logSeatAddition calls with BillingPeriodService.shouldApplyMonthlyProration (or an equivalent flag check) before writing seat logs.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/trpc/server/routers/viewer/teams/inviteMember/utils.ts, line 420:
<comment>Seat change logging now runs unconditionally, so seat logs are created even when the monthly-proration flag is off. This changes behavior despite the feature being gated; consider guarding these logSeatAddition calls with BillingPeriodService.shouldApplyMonthlyProration (or an equivalent flag check) before writing seat logs.</comment>
<file context>
@@ -415,6 +413,16 @@ export async function createNewUsersConnectToOrgIfExists({
+ if (createdUsers.length > 0) {
+ const seatTracker = new SeatChangeTrackingService();
+ const trackingTeamId = parentId ?? teamId;
+ await seatTracker.logSeatAddition({
+ teamId: trackingTeamId,
+ seatCount: createdUsers.length,
</file context>
packages/features/ee/billing/api/webhook/_customer.subscription.updated.ts
Show resolved
Hide resolved
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
This PR adds the monthly proration feature (gated behind feature flag): - Add operationId field to SeatChangeLog for idempotency - Add seat change tracking on member invites and deletions - Add MonthlyProrationService for processing prorated billing - Add payment webhooks for invoice success/failure tracking - Update subscription webhook to sync billing period on renewals - Update TeamBillingService to skip real-time updates when proration enabled The feature is gated by the 'monthly-proration' feature flag in BillingPeriodService.shouldApplyMonthlyProration() - when disabled, all behavior remains unchanged. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5d4de7e to
3f4680c
Compare
Only log seat changes when monthly-proration feature flag is enabled for the team. This prevents behavior changes when the feature is disabled. The check is performed in SeatChangeTrackingService.logSeatAddition/logSeatRemoval methods, so callers don't need to know about the feature flag.
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
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/billing/service/seatTracking/SeatChangeTrackingService.ts">
<violation number="1" location="packages/features/ee/billing/service/seatTracking/SeatChangeTrackingService.ts:50">
P2: This feature-flag gate prevents seat change logging when monthly proration is disabled, but the PR description says logging must run unconditionally for data collection. This will stop collecting baseline logs until the flag is enabled.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| operationId, | ||
| } = params; | ||
|
|
||
| // Only log seat changes if monthly proration is enabled |
There was a problem hiding this comment.
P2: This feature-flag gate prevents seat change logging when monthly proration is disabled, but the PR description says logging must run unconditionally for data collection. This will stop collecting baseline logs until the flag is enabled.
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/seatTracking/SeatChangeTrackingService.ts, line 50:
<comment>This feature-flag gate prevents seat change logging when monthly proration is disabled, but the PR description says logging must run unconditionally for data collection. This will stop collecting baseline logs until the flag is enabled.</comment>
<file context>
@@ -44,6 +46,14 @@ export class SeatChangeTrackingService {
operationId,
} = params;
+
+ // Only log seat changes if monthly proration is enabled
+ const billingPeriodService = new BillingPeriodService();
+ const shouldLog = await billingPeriodService.shouldApplyMonthlyProration(teamId);
</file context>
Add operationId field to SeatChangeLog table to prevent duplicate seat change logs from race conditions. - Add nullable operationId column - Add unique constraint on (teamId, operationId)
Devin AI is addressing Cubic AI's review feedbackA Devin session has been created to address the issues identified by Cubic AI. |
Mock shouldApplyMonthlyProration to return true so tests can verify seat logging behavior.
|
Closing this PR as it has been split into two smaller PRs for easier review:
Both PRs together contain the same functionality as this one. |
What does this PR do?
This PR adds the monthly proration feature (gated behind feature flag):
The feature is gated by the 'monthly-proration' feature flag in BillingPeriodService.shouldApplyMonthlyProration() - when disabled, all behavior remains unchanged.
Updates since last revision
Addressed Cubic AI review feedback:
selectclause to TeamBilling query to fetch only required fields (id,teamId) instead of pulling the full rowMandatory Tasks (DO NOT REMOVE)
How should this be tested?
monthly-prorationfeature flag is disabled - all existing behavior should remain unchangedHuman Review Checklist
inviteMember/utils.tscorrectly tracks additions across teams and organizationsLink to Devin run: https://app.devin.ai/sessions/795ceda9b16f4d649139e628c130064a
Requested by: unknown ()