Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,11 @@ export const POST = withRouteHandler(
if (!parsed.success) return parsed.response
try {
const actor = await getAdminAuditActor(request)
const { includedMonthlyDollars, usageLimitDollars, ...body } = parsed.data.body
const { usageLimitDollars, ...body } = parsed.data.body
return singleResponse(
toDashboardProvisioning(
await issueEnterpriseProvisioning({
...body,
includedMonthlyCredits: dollarsToCredits(includedMonthlyDollars),
usageLimitCredits:
usageLimitDollars === undefined ? undefined : dollarsToCredits(usageLimitDollars),
requestedByEmail: actor.email ?? 'admin-api',
Expand Down
2 changes: 0 additions & 2 deletions apps/sim/lib/api/contracts/v1/admin/dashboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ describe('admin dashboard credit grant contract', () => {
adminDashboardIssueEnterpriseBodySchema.safeParse({
ownerUserId: 'owner-1',
monthlyInvoiceAmountUsd: 500,
includedMonthlyDollars: 500,
seats: 10,
concurrencyLimit: 1250,
pausePaymentCollection: true,
Expand All @@ -90,7 +89,6 @@ describe('admin dashboard credit grant contract', () => {
adminDashboardIssueEnterpriseBodySchema.safeParse({
ownerUserId: 'owner-1',
monthlyInvoiceAmountUsd: 500,
includedMonthlyDollars: 500,
seats: 10,
concurrencyLimit: null,
}).success
Expand Down
1 change: 0 additions & 1 deletion apps/sim/lib/api/contracts/v1/admin/dashboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ export const adminDashboardIssueEnterpriseBodySchema = z.object({
ownerUserId: z.string().min(1),
organizationName: z.string().trim().min(1).max(120).optional(),
monthlyInvoiceAmountUsd: z.number().min(0.01).max(10_000_000).multipleOf(0.01),
Comment thread
icecrasher321 marked this conversation as resolved.
includedMonthlyDollars: creditAlignedDollarAmountSchema,
usageLimitDollars: creditAlignedDollarAmountSchema.optional(),
seats: z.number().int().positive().max(100_000),
concurrencyLimit: z.number().int().positive().max(MAX_BILLING_CONCURRENCY_LIMIT).optional(),
Expand Down
12 changes: 7 additions & 5 deletions apps/sim/lib/billing/enterprise-provisioning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,26 +160,28 @@ function context() {
}

describe('Enterprise issuance serialization decisions', () => {
it('preserves legacy request keys when no concurrency override is supplied', () => {
it('derives included usage from invoice amount in the request key', () => {
const input = {
ownerUserId: 'owner-1',
monthlyInvoiceAmountUsd: 125,
includedMonthlyCredits: 20000,
usageLimitCredits: 24000,
seats: 12,
requestedByEmail: 'admin@sim.ai',
requestedByUserId: 'admin-1',
}

expect(buildEnterpriseProvisioningRequestKey(input, 'org-1')).toBe(
'enterprise-v2:owner-1:org-1:12500:20000:24000:12'
'enterprise-v2:owner-1:org-1:12500:25000:24000:12'
)
expect(
buildEnterpriseProvisioningRequestKey({ ...input, concurrencyLimit: 1250 }, 'org-1')
).toBe('enterprise-v2:owner-1:org-1:12500:20000:24000:12:1250')
).toBe('enterprise-v2:owner-1:org-1:12500:25000:24000:12:1250')
expect(
buildEnterpriseProvisioningRequestKey({ ...input, pausePaymentCollection: true }, 'org-1')
).toBe('enterprise-v2:owner-1:org-1:12500:20000:24000:12:draft-collection')
).toBe('enterprise-v2:owner-1:org-1:12500:25000:24000:12:draft-collection')
expect(
buildEnterpriseProvisioningRequestKey({ ...input, usageLimitCredits: undefined }, 'org-1')
).toBe('enterprise-v2:owner-1:org-1:12500:25000:25000:12')
})

it('deduplicates an identical unresolved request to the existing outbox operation', () => {
Expand Down
12 changes: 7 additions & 5 deletions apps/sim/lib/billing/enterprise-provisioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { and, count, desc, eq, inArray, isNull, sql } from 'drizzle-orm'
import type Stripe from 'stripe'
import { parseBillingConcurrencyLimit } from '@/lib/billing/concurrency-defaults'
import { getBillingConcurrencyLimit } from '@/lib/billing/concurrency-limits'
import { dollarsToCredits } from '@/lib/billing/credits/conversion'
import {
deriveEnterpriseOperationStatus,
ENTERPRISE_METADATA_SYNC_EVENT_TYPE,
Expand Down Expand Up @@ -234,7 +235,6 @@ export interface IssueEnterpriseProvisioningInput {
ownerUserId: string
organizationName?: string
monthlyInvoiceAmountUsd: number
includedMonthlyCredits: number
usageLimitCredits?: number
seats: number
concurrencyLimit?: number
Expand Down Expand Up @@ -281,13 +281,14 @@ export function buildEnterpriseProvisioningRequestKey(
input: IssueEnterpriseProvisioningInput,
organizationId: string
): string {
const includedMonthlyCredits = dollarsToCredits(input.monthlyInvoiceAmountUsd)
Comment thread
icecrasher321 marked this conversation as resolved.
const requestTerms: Array<string | number> = [
'enterprise-v2',
input.ownerUserId,
organizationId,
Math.round(input.monthlyInvoiceAmountUsd * 100),
input.includedMonthlyCredits,
input.usageLimitCredits ?? input.includedMonthlyCredits,
includedMonthlyCredits,
input.usageLimitCredits ?? includedMonthlyCredits,
input.seats,
]
if (input.concurrencyLimit !== undefined) requestTerms.push(input.concurrencyLimit)
Expand Down Expand Up @@ -422,6 +423,7 @@ export async function issueEnterpriseProvisioning(
'Monthly invoice amount must be at least $0.01 and use whole cents'
)
}
const includedMonthlyCredits = dollarsToCredits(input.monthlyInvoiceAmountUsd)
if (
input.concurrencyLimit !== undefined &&
parseBillingConcurrencyLimit(input.concurrencyLimit) !== input.concurrencyLimit
Expand Down Expand Up @@ -548,8 +550,8 @@ export async function issueEnterpriseProvisioning(
requestedByEmail: input.requestedByEmail,
requestedByUserId: input.requestedByUserId,
invoiceAmountCents,
includedMonthlyCredits: input.includedMonthlyCredits,
usageLimitCredits: input.usageLimitCredits ?? input.includedMonthlyCredits,
includedMonthlyCredits,
usageLimitCredits: input.usageLimitCredits ?? includedMonthlyCredits,
seats: input.seats,
...(input.concurrencyLimit !== undefined ? { concurrencyLimit: input.concurrencyLimit } : {}),
pausePaymentCollection: input.pausePaymentCollection ?? false,
Expand Down
Loading