feat(plugins): sell khanegi plugins as priced subscriptions
Catalog prices, purchase requests, and setting-key activation live on SuperAdmin so module admins can buy add-ons. Also rewrite legacy nerkh logo hosts to the SuperAdmin upload origin. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -87,6 +87,7 @@ model Tenant {
|
||||
/// 0..N hosting/billing servers per sold version (not the same as nodeServer API URL)
|
||||
serverAccounts ServerAccount[]
|
||||
pluginSubscriptions PluginSubscription[]
|
||||
pluginPurchaseRequests PluginPurchaseRequest[]
|
||||
supportSubscriptions SupportSubscription[]
|
||||
settings Setting[]
|
||||
|
||||
@@ -311,6 +312,11 @@ model PluginSubscription {
|
||||
description String?
|
||||
alertDaysBefore Int @default(3) @map("alert_days_before")
|
||||
lastAlertDate DateTime? @map("last_alert_date")
|
||||
/// Khanegi catalog key (jibit_enabled, …). Null = custom / hamkar.
|
||||
settingKey String? @map("setting_key") @db.VarChar(50)
|
||||
autoCutOnExpiry Boolean @default(false) @map("auto_cut_on_expiry")
|
||||
/// Sent expiry SMS marks: { "expiresAtIso": { "3": true, "2": true, "1": true, "0": true } }
|
||||
alertMarks Json? @map("alert_marks")
|
||||
isActive Boolean @default(true) @map("is_active")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
@@ -321,9 +327,69 @@ model PluginSubscription {
|
||||
@@index([tenantId])
|
||||
@@index([expiresAt])
|
||||
@@index([isActive])
|
||||
@@index([settingKey])
|
||||
@@map("plugin_subscriptions")
|
||||
}
|
||||
|
||||
enum PluginPurchaseStatus {
|
||||
pending
|
||||
approved
|
||||
rejected
|
||||
cancelled
|
||||
}
|
||||
|
||||
model PluginCatalogItem {
|
||||
id String @id @default(cuid())
|
||||
settingKey String @unique @map("setting_key") @db.VarChar(50)
|
||||
titleFa String @map("title_fa")
|
||||
description String? @db.Text
|
||||
inStore Boolean @default(true) @map("in_store")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
prices PluginCatalogPrice[]
|
||||
requests PluginPurchaseRequest[]
|
||||
|
||||
@@map("plugin_catalog_items")
|
||||
}
|
||||
|
||||
model PluginCatalogPrice {
|
||||
id String @id @default(cuid())
|
||||
catalogItemId String @map("catalog_item_id")
|
||||
durationDays Int @map("duration_days")
|
||||
enabled Boolean @default(false)
|
||||
originalPriceRials Int @default(0) @map("original_price_rials")
|
||||
finalPriceRials Int @default(0) @map("final_price_rials")
|
||||
|
||||
catalogItem PluginCatalogItem @relation(fields: [catalogItemId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([catalogItemId, durationDays])
|
||||
@@map("plugin_catalog_prices")
|
||||
}
|
||||
|
||||
model PluginPurchaseRequest {
|
||||
id String @id @default(cuid())
|
||||
tenantId String @map("tenant_id")
|
||||
catalogItemId String @map("catalog_item_id")
|
||||
durationDays Int @map("duration_days")
|
||||
originalPriceRials Int @map("original_price_rials")
|
||||
finalPriceRials Int @map("final_price_rials")
|
||||
status PluginPurchaseStatus @default(pending)
|
||||
autoCutOnExpiry Boolean? @map("auto_cut_on_expiry")
|
||||
pluginSubscriptionId String? @map("plugin_subscription_id")
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
decidedAt DateTime? @map("decided_at")
|
||||
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
|
||||
catalogItem PluginCatalogItem @relation(fields: [catalogItemId], references: [id], onDelete: Restrict)
|
||||
|
||||
@@index([tenantId, status])
|
||||
@@index([catalogItemId])
|
||||
@@index([createdAt])
|
||||
@@map("plugin_purchase_requests")
|
||||
}
|
||||
|
||||
model PluginCharge {
|
||||
id String @id @default(cuid())
|
||||
pluginId String @map("plugin_id")
|
||||
|
||||
Reference in New Issue
Block a user