Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/prisma/schema/subscriptions.prisma
T

359 lines
14 KiB
Plaintext

enum SubscriptionProductKind {
base_plan
recurring_add_on
one_time_setup
contract_override
}
enum TenantProductAcquisitionStatus {
pending
provider_pending
payment_pending
active
failed
cancelled
expired
refunded
}
model SubscriptionProduct {
id String @id @default(cuid())
billingProviderId String
code String?
lagoPlanCode String?
kind SubscriptionProductKind @default(base_plan)
order Int
title String
active Boolean
model Int
public Boolean
groupTitle String?
groupDescription String?
description String?
badge String?
billingAddressCollection String @default("auto")
hasQuantity Boolean @default(false)
canBuyAgain Boolean @default(false)
prices SubscriptionPrice[]
features SubscriptionFeature[]
tenantProducts TenantSubscriptionProduct[]
acquisitions TenantProductAcquisition[]
usageBasedPrices SubscriptionUsageBasedPrice[]
@@index([code])
@@index([lagoPlanCode])
}
model SubscriptionPrice {
id String @id @default(cuid())
subscriptionProductId String
subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade)
billingProviderId String
lagoPlanCode String?
lagoFeeCode String?
molliePriceReference String?
billingCadence String @default("monthly")
contractMonths Int @default(1)
requiredPaymentMethod String @default("any")
type Int
billingPeriod Int
price Decimal
currency String
trialDays Int
active Boolean
tenantProductPrices TenantSubscriptionProductPrice[]
acquisitions TenantProductAcquisition[]
@@index([lagoPlanCode])
@@index([lagoFeeCode])
@@index([molliePriceReference])
}
model SubscriptionUsageBasedPrice {
id String @id @default(cuid())
subscriptionProductId String
subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade)
billingProviderId String
billingPeriod Int
currency String
unit String
unitTitle String
unitTitlePlural String
usageType String
aggregateUsage String
tiersMode String
billingScheme String
tiers SubscriptionUsageBasedTier[]
tenantProductPrices TenantSubscriptionProductPrice[]
}
model SubscriptionUsageBasedTier {
id String @id @default(cuid())
subscriptionUsageBasedPriceId String
subscriptionUsageBasedPrice SubscriptionUsageBasedPrice @relation(fields: [subscriptionUsageBasedPriceId], references: [id], onDelete: Cascade)
from Int
to Int?
perUnitPrice Decimal?
flatFeePrice Decimal?
}
model SubscriptionFeature {
id String @id @default(cuid())
subscriptionProductId String
order Int
title String
name String
code String?
type Int
value Int
quotaValue Json?
href String?
badge String?
accumulate Boolean @default(false)
subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id], onDelete: Cascade)
@@index([code])
}
model TenantSubscription {
id String @id @default(cuid())
tenantId String @unique
billingProviderCustomerId String?
lagoCustomerId String?
mollieCustomerId String?
planCode String?
seatQuantity Int @default(1)
officeSeatQuantity Int @default(0)
fieldSeatQuantity Int @default(0)
billingCadence String @default("monthly")
commitmentEndsAt DateTime?
storageAddOnCodes String[] @default([])
lastInvoiceExternalId String?
lastPaymentExternalId String?
status String @default("trial")
trialStartsAt DateTime?
trialEndsAt DateTime?
pastDueSince DateTime?
frozenAt DateTime?
cancelledAt DateTime?
dunningAttemptCount Int @default(0)
lastDunningSentAt DateTime?
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
products TenantSubscriptionProduct[]
usageRecords TenantSubscriptionUsageRecord[]
@@index([status])
@@index([planCode])
@@index([lagoCustomerId])
@@index([mollieCustomerId])
@@index([trialEndsAt])
@@index([pastDueSince])
@@index([frozenAt])
}
model TenantSubscriptionProduct {
id String @id @default(cuid())
createdAt DateTime @default(now())
tenantSubscriptionId String
subscriptionProductId String
customEntitlementPlanId String?
cancelledAt DateTime?
endsAt DateTime?
billingProviderSubscriptionId String?
lagoSubscriptionId String?
planCode String?
seatQuantity Int?
officeSeatQuantity Int?
fieldSeatQuantity Int?
billingCadence String @default("monthly")
commitmentEndsAt DateTime?
storageAddOnCodes String[] @default([])
quantity Int?
fromCheckoutSessionId String?
currentPeriodStart DateTime?
currentPeriodEnd DateTime?
tenantSubscription TenantSubscription @relation(fields: [tenantSubscriptionId], references: [id], onDelete: Cascade)
subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id])
prices TenantSubscriptionProductPrice[]
acquisition TenantProductAcquisition?
@@index([lagoSubscriptionId])
@@index([planCode])
}
/// Idempotent tenant-owned ledger for products acquired outside the one-base-plan
/// lifecycle. `scopeKey` is the stable business scope (for example `tenant` or a
/// normalized hostname) and makes provider retries safe without granting the same
/// entitlement twice. Provider failures remain local to this row and must never
/// mutate the base TenantSubscription status.
model TenantProductAcquisition {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
subscriptionProductId String
subscriptionPriceId String?
productKind SubscriptionProductKind
scopeKey String
requestHash String
status TenantProductAcquisitionStatus @default(pending)
version Int @default(1)
quantity Int @default(1)
amountCents Int?
currency String?
checkoutSessionId String? @unique
tenantSubscriptionProductId String? @unique
provider String?
providerAttempt Int @default(1)
providerPaymentId String? @unique
lagoInvoiceIntentReference String?
providerPaymentExpiresAt DateTime?
providerPaidAt DateTime?
invoiceExternalId String?
lagoSubscriptionId String?
lagoProviderSubscriptionId String?
initialLagoInvoiceExternalId String? @unique
checkoutUrl String?
sourceReference String?
failureCode String?
providerStartedAt DateTime?
reservationExpiresAt DateTime?
activatedAt DateTime?
failedAt DateTime?
cancelledAt DateTime?
expiredAt DateTime?
refundedAt DateTime?
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
subscriptionProduct SubscriptionProduct @relation(fields: [subscriptionProductId], references: [id])
subscriptionPrice SubscriptionPrice? @relation(fields: [subscriptionPriceId], references: [id])
checkoutSession CheckoutSessionStatus? @relation(fields: [checkoutSessionId], references: [id], onDelete: SetNull)
tenantSubscriptionProduct TenantSubscriptionProduct? @relation(fields: [tenantSubscriptionProductId], references: [id], onDelete: SetNull)
@@unique([tenantId, subscriptionProductId, scopeKey])
@@index([tenantId, status, updatedAt])
@@index([tenantId, status, reservationExpiresAt])
@@index([subscriptionProductId, productKind, status])
@@index([invoiceExternalId])
@@index([lagoSubscriptionId])
@@index([lagoProviderSubscriptionId])
}
model TenantSubscriptionProductPrice {
id String @id @default(cuid())
tenantSubscriptionProductId String
tenantSubscriptionProduct TenantSubscriptionProduct @relation(fields: [tenantSubscriptionProductId], references: [id], onDelete: Cascade)
subscriptionPriceId String?
subscriptionPrice SubscriptionPrice? @relation(fields: [subscriptionPriceId], references: [id])
subscriptionUsageBasedPriceId String?
subscriptionUsageBasedPrice SubscriptionUsageBasedPrice? @relation(fields: [subscriptionUsageBasedPriceId], references: [id])
quantity Int @default(1)
isMetered Boolean @default(false)
}
model TenantSubscriptionUsageRecord {
id String @id @default(cuid())
tenantId String
tenantSubscriptionId String
tenantSubscriptionProductPriceId String
tenantSubscription TenantSubscription @relation(fields: [tenantSubscriptionId], references: [id], onDelete: Cascade)
metricCode String @default("legacy")
timestamp Int
quantity Int
idempotencyKey String?
billingProviderSubscriptionItemId String?
@@unique([tenantId, metricCode, idempotencyKey])
@@index([tenantId, metricCode, timestamp])
}
model TenantProductConfiguration {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String @unique
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
blueprintId String @default("crm-saas")
notes String?
signupSubscriptionPriceId String?
signupOfficeSeatQuantity Int?
signupFieldSeatQuantity Int?
}
model TenantFeatureOverride {
id String @id @default(cuid())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
featureKey String
override String
reason String?
@@unique([tenantId, featureKey])
@@index([featureKey, override])
}
model CheckoutSessionStatus {
id String @id
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
pending Boolean @default(true) // has not added products to tenant
email String
fromUrl String
fromUserId String?
createdUserId String?
createdTenantId String?
provider String?
providerPaymentId String?
invoiceExternalId String?
subscriptionProductId String?
subscriptionPriceId String?
lagoSubscriptionId String?
planCode String?
productKind SubscriptionProductKind @default(base_plan)
scopeKey String?
quantity Int @default(1)
officeSeatQuantity Int @default(0)
fieldSeatQuantity Int @default(0)
billingCadence String @default("monthly")
commitmentEndsAt DateTime?
storageAddOnCodes String[] @default([])
amountCents Int?
currency String?
checkoutUrl String?
status String @default("open")
productAcquisition TenantProductAcquisition?
@@index([providerPaymentId])
@@index([tenantId, pending])
}
model BillingWebhookLog {
id String @id @default(cuid())
createdAt DateTime @default(now())
providerEventId String @unique
eventType String
processedAt DateTime?
relatedTenantId String?
error String?
@@index([eventType, createdAt])
@@index([relatedTenantId, createdAt])
}
model Credit {
id String @id @default(cuid())
createdAt DateTime @default(now())
tenantId String
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
userId String?
user User? @relation(fields: [userId], references: [id])
amount Int
type String
objectId String?
@@index([tenantId, userId])
@@index([tenantId, createdAt])
}