fix(billing): credit signupGrant on the generic org-creation path#3949
Conversation
createOrganizationForUser credited the one-shot signupGrant, but the generic POST /api/organizations path (organizations.crud.service.create) did not, so an org created there (e.g. via a manual "create workspace" screen) started at 0 balance on a plan that defines a grant. Call the existing idempotent BillingSignupGrantService.grantOnSignup from crud.create too, best-effort and outside the rollback try/catch. Add a config-driven backfill migration: for every plan in planDefinitions with a positive signupGrant, credit orgs on that plan that lack a signup_grant ledger entry. Generalises the 20260511 backfill (which hardcoded 500 and enumerated the subscriptions collection, missing orgs with no subscription document) by enumerating organizations and reading the configured amount. Idempotent via the signup_grant-<orgId> refId. Claude-Session: https://claude.ai/code/session_01S8zb8xNiyALVu366vzyi8x
|
Warning Review limit reached
Next review available in: 6 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (3)
WalkthroughThis PR adds a database migration to backfill missing signup_grant ledger entries for organizations under plans with a positive signupGrant, and wires a post-creation call to BillingSignupGrantService.grantOnSignup into the organization creation flow, with accompanying unit tests. ChangesSignup Grant Backfill and Wiring
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant OrgCrudService
participant MembershipsRepo
participant BillingSignupGrantService
Client->>OrgCrudService: create(orgData)
OrgCrudService->>MembershipsRepo: create owner membership / set current org
MembershipsRepo-->>OrgCrudService: success
OrgCrudService->>BillingSignupGrantService: grantOnSignup(orgId, planId)
BillingSignupGrantService-->>OrgCrudService: best-effort result (no rollback)
OrgCrudService-->>Client: created organization
Related PRs: None identified. Suggested labels: billing, organizations, migration, tests Suggested reviewers: None identified. 🐰 A grant hops in when orgs are born, 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #3949 +/- ##
=======================================
Coverage 92.70% 92.70%
=======================================
Files 169 169
Lines 5562 5563 +1
Branches 1789 1791 +2
=======================================
+ Hits 5156 5157 +1
Misses 326 326
Partials 80 80
Flags with carried forward coverage won't be shown. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@modules/billing/migrations/20260707100000-backfill-missing-signup-grant-credits.js`:
- Around line 64-118: The migration’s redundant `existing` pre-check in the `for
await (const org of cursor)` loop is causing an extra DB round trip per
organization. Remove the `extraBalances.findOne` lookup and rely on the atomic
`extraBalances.updateOne` with the `'ledger.refId': { $ne: idempotencyKey }`
guard plus `result.modifiedCount` to handle both existing grants and concurrent
writers in `backfill-missing-signup-grant-credits.js`.
In `@modules/organizations/tests/organizations.crud.grant.unit.tests.js`:
- Around line 67-89: Add a negative-path test in
organizations.crud.grant.unit.tests.js to verify the signup grant is not issued
when org/membership setup fails. Extend the existing OrgCrudService.create
coverage by mocking a failure in MembershipRepository.create or
UserService.updateById, then assert that grantOnSignup is never called and the
create call propagates/handles the error as expected. Use the existing
OrgCrudService.create and mockGrantOnSignup setup so the new test clearly checks
the rollback path before the grant step.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: d6715605-68ff-48f9-a1c3-0b7b03ecb336
📒 Files selected for processing (3)
modules/billing/migrations/20260707100000-backfill-missing-signup-grant-credits.jsmodules/organizations/services/organizations.crud.service.jsmodules/organizations/tests/organizations.crud.grant.unit.tests.js
…nt path The migration re-implemented the ledger write with the raw driver keyed on a STRING organization, but billingextrabalances.organization is Schema.ObjectId and every repository write casts to ObjectId — the string write would have created orphan, app-invisible ghost documents and silently credited no one while logging success. Delegate to BillingSignupGrantService.grantOnSignup per org so the migration reuses the exact runtime grant path: Mongoose ObjectId casting, the plan co-presence guard (getActivePlan), Zod amount validation (creditGrant), and refId idempotency. Add an integration test proving an ObjectId-keyed org is credited (no string ghost doc), idempotent re-runs, and already-granted orgs are skipped. Resolves the critical + two mediums raised in pre-merge review. Claude-Session: https://claude.ai/code/session_01S8zb8xNiyALVu366vzyi8x
Cover the rollback path: when MembershipRepository.create throws, create() re-throws before reaching the grant call, so grantOnSignup must never fire. Locks in the placement of the best-effort grant after the rollback try/catch. Claude-Session: https://claude.ai/code/session_01S8zb8xNiyALVu366vzyi8x
What
createOrganizationForUser(invite/verify signup) credited the configuredsignupGrant, but the genericPOST /api/organizationspath (organizations.crud.service.create), used by a manual "create workspace" flow, did not. A fresh org created there started at 0 balance on a plan that defines a grant.Changes
organizations.crud.service.create: call the existing idempotentBillingSignupGrantService.grantOnSignup, best-effort, outside the rollback try/catch (mirrorsorganizations.service.js::createOrganizationForUser).planDefinitionswith a positivesignupGrant, credit orgs on that plan lacking asignup_grantledger entry. Generalises the 20260511 backfill (hardcoded 500, keyed offsubscriptions, missing orgs with no subscription doc) by enumeratingorganizationsand reading the configured amount. Idempotent viasignup_grant-<orgId>.crud.create.Notes
down()is an intentional no-op —signup_grantentries aren't attributable to this migration; a blanket $pull would revert legitimately-earned grants.https://claude.ai/code/session_01S8zb8xNiyALVu366vzyi8x
Summary by CodeRabbit
New Features
Bug Fixes