You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

737 lines
20 KiB

generator client {
provider = "prisma-client-js"
previewFeatures = []
binaryTargets = ["debian-openssl-3.0.x", "linux-arm64-openssl-3.0.x", "native"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Access {
alias String?
createdAt DateTime @default(now())
granteeUser User? @relation("accessGet", fields: [granteeUserId], onDelete: Cascade, references: [id])
granteeUserId String?
id String @id @default(uuid())
permissions AccessPermission[] @default([READ_RESTRICTED])
settings Json @default("{}")
updatedAt DateTime @updatedAt
userId String
user User @relation("accessGive", fields: [userId], onDelete: Cascade, references: [id])
@@index([alias])
@@index([granteeUserId])
@@index([userId])
}
model Account {
activities Order[]
balance Float @default(0)
balances AccountBalance[]
comment String?
createdAt DateTime @default(now())
currency String?
id String @default(uuid())
isExcluded Boolean @default(false)
name String?
ownerships Ownership[]
platform Platform? @relation(fields: [platformId], references: [id])
platformId String?
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String
@@id([id, userId])
@@index([currency])
@@index([id])
@@index([isExcluded])
@@index([name])
@@index([userId])
}
model AccountBalance {
account Account @relation(fields: [accountId, userId], onDelete: Cascade, references: [id, userId])
accountId String
createdAt DateTime @default(now())
date DateTime @default(now())
id String @id @default(uuid())
updatedAt DateTime @updatedAt
userId String
value Float
@@unique([accountId, date])
@@index([accountId])
@@index([date])
}
model Analytics {
activityCount Int @default(0)
country String?
dataProviderGhostfolioDailyRequests Int @default(0)
lastRequestAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String @id
@@index([lastRequestAt])
@@index([updatedAt])
}
model ApiKey {
createdAt DateTime @default(now())
hashedKey String @unique
id String @id @default(uuid())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String
@@index([userId])
}
model AssetProfileResolution {
createdAt DateTime @default(now())
currency String
dataSourceOrigin DataSource
dataSourceTarget DataSource
id String @id @default(uuid())
requestCount Int @default(1)
symbolOrigin String
symbolTarget String
updatedAt DateTime @updatedAt
@@unique([dataSourceOrigin, symbolOrigin])
}
model AuthDevice {
createdAt DateTime @default(now())
credentialId Bytes
credentialPublicKey Bytes
counter Int
id String @id @default(uuid())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String
@@index([userId])
}
model MarketData {
createdAt DateTime @default(now())
dataSource DataSource
date DateTime
id String @id @default(uuid())
marketPrice Float
state MarketDataState @default(CLOSE)
symbol String
@@unique([dataSource, date, symbol])
@@index([dataSource])
@@index([dataSource, symbol])
@@index([date])
@@index([marketPrice])
@@index([state])
@@index([symbol])
}
model Order {
account Account? @relation(fields: [accountId, accountUserId], references: [id, userId])
accountId String?
accountUserId String?
comment String?
createdAt DateTime @default(now())
currency String?
date DateTime
fee Float
id String @id @default(uuid())
isDraft Boolean @default(false)
quantity Float
symbolProfileId String
tags Tag[]
type Type
unitPrice Float
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String
SymbolProfile SymbolProfile @relation(fields: [symbolProfileId], references: [id])
@@index([accountId])
@@index([date])
@@index([isDraft])
@@index([userId])
}
model Platform {
accounts Account[]
id String @id @default(uuid())
name String?
url String @unique
@@index([name])
}
model Property {
key String @id
value String
}
model Settings {
settings Json?
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String @id
}
model SymbolProfile {
activities Order[]
assetClass AssetClass?
assetSubClass AssetSubClass?
comment String?
countries Json?
createdAt DateTime @default(now())
currency String
cusip String?
dataSource DataSource
figi String?
figiComposite String?
figiShareClass String?
holdings Json? @default("[]")
id String @id @default(uuid())
isActive Boolean @default(true)
isin String?
name String?
updatedAt DateTime @updatedAt
scraperConfiguration Json?
sectors Json?
symbol String
symbolMapping Json?
url String?
user User? @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String?
watchedBy User[] @relation("UserWatchlist")
SymbolProfileOverrides SymbolProfileOverrides?
@@unique([dataSource, symbol])
@@index([assetClass])
@@index([currency])
@@index([cusip])
@@index([dataSource])
@@index([isActive])
@@index([isin])
@@index([name])
@@index([symbol])
}
model SymbolProfileOverrides {
assetClass AssetClass?
assetSubClass AssetSubClass?
countries Json? @default("[]")
holdings Json? @default("[]")
name String?
sectors Json? @default("[]")
symbolProfileId String @id
updatedAt DateTime @updatedAt
url String?
SymbolProfile SymbolProfile @relation(fields: [symbolProfileId], onDelete: Cascade, references: [id])
}
model Subscription {
createdAt DateTime @default(now())
expiresAt DateTime
id String @id @default(uuid())
price Float?
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String
@@index([userId])
}
model Tag {
activities Order[]
id String @id @default(uuid())
name String
user User? @relation(fields: [userId], onDelete: Cascade, references: [id])
userId String?
@@unique([name, userId])
@@index([name])
}
model User {
accessesGet Access[] @relation("accessGet")
accessesGive Access[] @relation("accessGive")
accessToken String?
accounts Account[]
activities Order[]
analytics Analytics?
apiKeys ApiKey[]
authChallenge String?
authDevices AuthDevice[]
createdAt DateTime @default(now())
entities Entity[]
id String @id @default(uuid())
partnerships Partnership[]
provider Provider @default(ANONYMOUS)
role Role @default(USER)
settings Settings?
subscriptions Subscription[]
tags Tag[]
thirdPartyId String?
updatedAt DateTime @updatedAt
watchlist SymbolProfile[] @relation("UserWatchlist")
SymbolProfile SymbolProfile[]
k1ImportSessions K1ImportSession[]
@@index([accessToken])
@@index([createdAt])
@@index([provider])
@@index([role])
@@index([thirdPartyId])
}
enum AccessPermission {
READ
READ_RESTRICTED
}
enum AssetClass {
ALTERNATIVE_INVESTMENT
COMMODITY
EQUITY
FIXED_INCOME
LIQUIDITY
REAL_ESTATE
}
enum AssetSubClass {
BOND
CASH
COLLECTIBLE
COMMODITY
CRYPTOCURRENCY
ETF
MUTUALFUND
PRECIOUS_METAL
PRIVATE_EQUITY
STOCK
}
enum DataSource {
ALPHA_VANTAGE
COINGECKO
EOD_HISTORICAL_DATA
FINANCIAL_MODELING_PREP
GHOSTFOLIO
GOOGLE_SHEETS
MANUAL
RAPID_API
YAHOO
}
enum MarketDataState {
CLOSE
INTRADAY
}
enum Provider {
ANONYMOUS
GOOGLE
INTERNET_IDENTITY
OIDC
}
enum Role {
ADMIN
DEMO
INACTIVE
USER
}
enum Type {
BUY
DIVIDEND
FEE
INTEREST
LIABILITY
SELL
}
enum ViewMode {
DEFAULT
ZEN
}
enum EntityType {
INDIVIDUAL
TRUST
LLC
LP
CORPORATION
FOUNDATION
ESTATE
}
enum PartnershipType {
LP
GP
LLC
JOINT_VENTURE
FUND
}
enum DistributionType {
INCOME
RETURN_OF_CAPITAL
CAPITAL_GAIN
GUARANTEED_PAYMENT
DIVIDEND
INTEREST
}
enum KDocumentType {
K1
K3
}
enum KDocumentStatus {
DRAFT
ESTIMATED
FINAL
}
enum FamilyOfficeAssetType {
PUBLIC_EQUITY
PRIVATE_EQUITY
REAL_ESTATE
HEDGE_FUND
VENTURE_CAPITAL
FIXED_INCOME
COMMODITY
ART_COLLECTIBLE
CRYPTOCURRENCY
CASH
OTHER
}
enum ValuationSource {
APPRAISAL
MARKET
MANUAL
NAV_STATEMENT
FUND_ADMIN
}
enum DocumentType {
K1
K3
CAPITAL_CALL
DISTRIBUTION_NOTICE
NAV_STATEMENT
APPRAISAL
TAX_RETURN
SUBSCRIPTION_AGREEMENT
OTHER
}
model Entity {
id String @id @default(uuid())
name String
type EntityType
taxId String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
ownerships Ownership[]
memberships PartnershipMembership[]
distributionsReceived Distribution[]
documents Document[]
@@index([name])
@@index([type])
@@index([userId])
}
model Partnership {
id String @id @default(uuid())
name String
type PartnershipType
inceptionDate DateTime
fiscalYearEnd Int @default(12)
currency String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
userId String
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
members PartnershipMembership[]
assets PartnershipAsset[]
valuations PartnershipValuation[]
distributions Distribution[]
kDocuments KDocument[]
documents Document[]
importSessions K1ImportSession[]
boxOverrides K1BoxOverride[]
@@index([name])
@@index([type])
@@index([userId])
}
model PartnershipMembership {
id String @id @default(uuid())
entityId String
entity Entity @relation(fields: [entityId], onDelete: Cascade, references: [id])
partnershipId String
partnership Partnership @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
ownershipPercent Decimal
capitalCommitment Decimal?
capitalContributed Decimal?
classType String?
effectiveDate DateTime
endDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([entityId, partnershipId, effectiveDate])
@@index([entityId])
@@index([partnershipId])
}
model Ownership {
id String @id @default(uuid())
entityId String
entity Entity @relation(fields: [entityId], onDelete: Cascade, references: [id])
accountId String
accountUserId String
account Account @relation(fields: [accountId, accountUserId], onDelete: Cascade, references: [id, userId])
ownershipPercent Decimal
acquisitionDate DateTime?
costBasis Decimal?
effectiveDate DateTime
endDate DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([entityId, accountId, accountUserId, effectiveDate])
@@index([entityId])
@@index([accountId])
}
model Distribution {
id String @id @default(uuid())
partnershipId String?
partnership Partnership? @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
entityId String
entity Entity @relation(fields: [entityId], onDelete: Cascade, references: [id])
type DistributionType
amount Decimal
date DateTime
currency String
taxWithheld Decimal? @default(0)
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([partnershipId])
@@index([entityId])
@@index([date])
}
model KDocument {
id String @id @default(uuid())
partnershipId String
partnership Partnership @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
type KDocumentType
taxYear Int
filingStatus KDocumentStatus @default(DRAFT)
data Json?
documentFileId String?
documentFile Document? @relation(fields: [documentFileId], references: [id])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
importSession K1ImportSession?
lineItems K1LineItem[]
@@unique([partnershipId, type, taxYear])
@@index([partnershipId])
@@index([taxYear])
}
model PartnershipAsset {
id String @id @default(uuid())
partnershipId String
partnership Partnership @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
assetType FamilyOfficeAssetType
name String
description String?
acquisitionDate DateTime?
acquisitionCost Decimal?
currentValue Decimal?
currency String
metadata Json?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
valuations AssetValuation[]
@@index([partnershipId])
@@index([assetType])
}
model AssetValuation {
id String @id @default(uuid())
partnershipAssetId String
partnershipAsset PartnershipAsset @relation(fields: [partnershipAssetId], onDelete: Cascade, references: [id])
date DateTime
value Decimal
source ValuationSource
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([partnershipAssetId, date])
@@index([partnershipAssetId])
@@index([date])
}
model PartnershipValuation {
id String @id @default(uuid())
partnershipId String
partnership Partnership @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
date DateTime
nav Decimal
source ValuationSource
notes String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([partnershipId, date])
@@index([partnershipId])
@@index([date])
}
model Document {
id String @id @default(uuid())
entityId String?
entity Entity? @relation(fields: [entityId], onDelete: Cascade, references: [id])
partnershipId String?
partnership Partnership? @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
type DocumentType
name String
filePath String
fileSize Int?
mimeType String?
taxYear Int?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
kDocuments KDocument[]
k1ImportSessions K1ImportSession[]
@@index([entityId])
@@index([partnershipId])
}
enum K1ImportStatus {
PROCESSING
EXTRACTED
VERIFIED
CONFIRMED
CANCELLED
FAILED
}
model K1ImportSession {
id String @id @default(uuid())
partnershipId String
partnership Partnership @relation(fields: [partnershipId], onDelete: Cascade, references: [id])
userId String
user User @relation(fields: [userId], onDelete: Cascade, references: [id])
status K1ImportStatus @default(PROCESSING)
taxYear Int
fileName String
fileSize Int
extractionMethod String
rawExtraction Json?
documentId String?
document Document? @relation(fields: [documentId], references: [id])
kDocumentId String? @unique
kDocument KDocument? @relation(fields: [kDocumentId], references: [id])
errorMessage String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([partnershipId, taxYear])
@@index([userId])
}
/// Global IRS K-1 box reference. One row per unique box identifier.
/// Replaces the global (partnershipId = null) CellMapping rows.
/// NOTE: COMMENT ON annotations added in migration SQL for LLM discoverability.
model K1BoxDefinition {
boxKey String @id @map("box_key")
label String
section String?
dataType String @default("number") @map("data_type")
sortOrder Int @map("sort_order")
irsFormLine String? @map("irs_form_line")
description String?
isCustom Boolean @default(false) @map("is_custom")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
lineItems K1LineItem[]
overrides K1BoxOverride[]
@@map("k1_box_definition")
@@index([section])
@@index([sortOrder])
}
/// Per-partnership display overrides for a K1BoxDefinition.
/// Controls custom labels, ignored status, etc. Does NOT affect data integrity.
/// Replaces the per-partnership (partnershipId != null) CellMapping rows.
model K1BoxOverride {
id String @id @default(uuid())
boxKey String @map("box_key")
boxDefinition K1BoxDefinition @relation(fields: [boxKey], references: [boxKey], onDelete: Cascade)
partnershipId String @map("partnership_id")
partnership Partnership @relation(fields: [partnershipId], references: [id], onDelete: Cascade)
customLabel String? @map("custom_label")
isIgnored Boolean @default(false) @map("is_ignored")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([boxKey, partnershipId])
@@map("k1_box_override")
@@index([partnershipId])
}
/// Individual financial line item from an IRS Schedule K-1.
/// Fact table: one row per box per K-1 document.
/// NOTE: Partial unique index "k1_line_item_active_unique" on (k_document_id, box_key)
/// WHERE is_superseded = false — managed in migration SQL, not expressible in Prisma.
model K1LineItem {
id String @id @default(uuid())
kDocumentId String @map("k_document_id")
kDocument KDocument @relation(fields: [kDocumentId], references: [id], onDelete: Cascade)
boxKey String @map("box_key")
boxDefinition K1BoxDefinition @relation(fields: [boxKey], references: [boxKey])
amount Decimal? @db.Decimal(15, 6)
textValue String? @map("text_value")
rawText String? @map("raw_text")
confidence Decimal? @db.Decimal(3, 2)
sourcePage Int? @map("source_page")
sourceCoords Json? @map("source_coords")
isUserEdited Boolean @default(false) @map("is_user_edited")
isSuperseded Boolean @default(false) @map("is_superseded")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("k1_line_item")
@@index([kDocumentId, boxKey])
@@index([kDocumentId])
@@index([boxKey])
@@index([isSuperseded])
}