2072 lines
71 KiB
Plaintext
2072 lines
71 KiB
Plaintext
// Prisma schema for AI-native ERP Platform
|
|
// Multi-tenant, comprehensive financial and operational management system
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
// ============================================================================
|
|
// ENUMS
|
|
// ============================================================================
|
|
|
|
enum UserStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
SUSPENDED
|
|
PENDING_ACTIVATION
|
|
}
|
|
|
|
enum RoleType {
|
|
CEO
|
|
CFO
|
|
CONTROLLER
|
|
MANAGER
|
|
ANALYST
|
|
CLERK
|
|
CUSTOM
|
|
}
|
|
|
|
enum PermissionModule {
|
|
DASHBOARD
|
|
CHART_OF_ACCOUNTS
|
|
JOURNAL_ENTRIES
|
|
INVOICES
|
|
PAYMENTS
|
|
VENDORS
|
|
CUSTOMERS
|
|
BANK_ACCOUNTS
|
|
TREASURY
|
|
REPORTS
|
|
SETTINGS
|
|
USERS
|
|
INTEGRATIONS
|
|
AUDIT_LOG
|
|
}
|
|
|
|
enum PermissionAction {
|
|
VIEW
|
|
CREATE
|
|
EDIT
|
|
APPROVE
|
|
DELETE
|
|
ADMIN
|
|
}
|
|
|
|
enum InvoiceStatus {
|
|
DRAFT
|
|
PENDING_APPROVAL
|
|
APPROVED
|
|
PARTIALLY_PAID
|
|
PAID
|
|
OVERDUE
|
|
CANCELLED
|
|
VOID
|
|
}
|
|
|
|
enum PaymentStatus {
|
|
DRAFT
|
|
SCHEDULED
|
|
PROCESSING
|
|
COMPLETED
|
|
FAILED
|
|
REVERSED
|
|
CANCELLED
|
|
}
|
|
|
|
enum PaymentMethod {
|
|
ACH
|
|
WIRE
|
|
CHECK
|
|
CREDIT_CARD
|
|
BANK_TRANSFER
|
|
CASH
|
|
OTHER
|
|
}
|
|
|
|
enum InvoiceType {
|
|
ACCOUNTS_PAYABLE
|
|
ACCOUNTS_RECEIVABLE
|
|
}
|
|
|
|
enum AccountType {
|
|
ASSET
|
|
LIABILITY
|
|
EQUITY
|
|
REVENUE
|
|
EXPENSE
|
|
GAIN_LOSS
|
|
}
|
|
|
|
enum FiscalPeriodStatus {
|
|
OPEN
|
|
CLOSED
|
|
LOCKED
|
|
}
|
|
|
|
enum ConnectorStatus {
|
|
ACTIVE
|
|
INACTIVE
|
|
ERROR
|
|
PAUSED
|
|
TESTING
|
|
}
|
|
|
|
enum ConnectorType {
|
|
BANK_FEED
|
|
ACCOUNTING_SOFTWARE
|
|
ERP_SYSTEM
|
|
PAYMENT_PROCESSOR
|
|
DOCUMENT_STORAGE
|
|
CUSTOM_API
|
|
}
|
|
|
|
enum ConnectorHealthStatus {
|
|
HEALTHY
|
|
WARNING
|
|
ERROR
|
|
UNKNOWN
|
|
}
|
|
|
|
enum AIActionType {
|
|
CATEGORIZATION
|
|
ANOMALY_DETECTION
|
|
RECONCILIATION
|
|
FORECAST
|
|
RECOMMENDATION
|
|
DATA_TRANSFORMATION
|
|
VALIDATION
|
|
HEALING
|
|
}
|
|
|
|
enum AIActionStatus {
|
|
PENDING
|
|
EXECUTED
|
|
APPROVED
|
|
REJECTED
|
|
IN_PROGRESS
|
|
COMPLETED
|
|
FAILED
|
|
}
|
|
|
|
enum TransactionType {
|
|
DEBIT
|
|
CREDIT
|
|
REVERSAL
|
|
ADJUSTMENT
|
|
}
|
|
|
|
enum EntityType {
|
|
SUBSIDIARY
|
|
DIVISION
|
|
DEPARTMENT
|
|
LOCATION
|
|
COST_CENTER
|
|
}
|
|
|
|
// ============================================================================
|
|
// MULTI-TENANCY & ORGANIZATION
|
|
// ============================================================================
|
|
|
|
model Organization {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
name String @db.VarChar(255)
|
|
legalName String? @db.VarChar(255)
|
|
taxId String? @db.VarChar(50)
|
|
website String? @db.VarChar(255)
|
|
description String? @db.Text
|
|
logoUrl String? @db.VarChar(500)
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
industry String? @db.VarChar(100)
|
|
|
|
// Fiscal configuration
|
|
currentFiscalYearId String? @db.Uuid
|
|
fiscalYearStartMonth Int @default(1) // 1-12
|
|
fiscalYearStartDay Int @default(1) // 1-31
|
|
|
|
// Settings
|
|
defaultCurrency String @default("USD") @db.VarChar(3)
|
|
timezone String @default("UTC") @db.VarChar(50)
|
|
language String @default("en") @db.VarChar(10)
|
|
dateFormat String @default("YYYY-MM-DD") @db.VarChar(20)
|
|
|
|
// Metadata
|
|
settings Json?
|
|
customFields Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
entities Entity[]
|
|
users User[]
|
|
roles Role[]
|
|
permissions Permission[]
|
|
groups Group[]
|
|
chartOfAccounts ChartOfAccount[]
|
|
journalEntries JournalEntry[]
|
|
journalEntryLines JournalEntryLine[]
|
|
fiscalPeriods FiscalPeriod[]
|
|
invoices Invoice[]
|
|
payments Payment[]
|
|
paymentAllocations PaymentAllocation[]
|
|
vendors Vendor[]
|
|
customers Customer[]
|
|
bankAccounts BankAccount[]
|
|
bankTransactions BankTransaction[]
|
|
bankReconciliations BankReconciliation[]
|
|
currencies Currency[]
|
|
connectors Connector[]
|
|
aiActions AIAction[]
|
|
dashboardConfigs DashboardConfig[]
|
|
auditLogs AuditLog[]
|
|
shareholders Shareholder[]
|
|
leases Lease[]
|
|
budgetLines BudgetLine[]
|
|
purchaseOrders PurchaseOrder[]
|
|
documents Document[]
|
|
subscriptions Subscription[]
|
|
forecasts Forecast[]
|
|
drivers PlanningDriver[]
|
|
planningScenarios PlanningScenario[]
|
|
closePeriods ClosePeriod[]
|
|
closeTasks CloseTask[]
|
|
revenueContracts RevenueContract[]
|
|
revenueScheduleLines RevenueScheduleLine[]
|
|
fixedAssets FixedAsset[]
|
|
eveCharacters EveCharacter[]
|
|
|
|
@@index([name])
|
|
@@index([status])
|
|
@@map("organizations")
|
|
}
|
|
|
|
model Entity {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
type EntityType @default(SUBSIDIARY)
|
|
code String @db.VarChar(50)
|
|
parentEntityId String? @db.Uuid
|
|
description String? @db.Text
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
// Settings
|
|
defaultCurrency String @default("USD") @db.VarChar(3)
|
|
isConsolidated Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
parentEntity Entity? @relation("EntityHierarchy", fields: [parentEntityId], references: [id], onDelete: SetNull)
|
|
childEntities Entity[] @relation("EntityHierarchy")
|
|
journalEntryLines JournalEntryLine[]
|
|
invoices Invoice[]
|
|
bankAccounts BankAccount[]
|
|
|
|
@@index([organizationId])
|
|
@@index([parentEntityId])
|
|
@@index([code])
|
|
@@map("entities")
|
|
}
|
|
|
|
// ============================================================================
|
|
// AUTHENTICATION & RBAC
|
|
// ============================================================================
|
|
|
|
model User {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
email String @db.VarChar(255)
|
|
emailVerified DateTime?
|
|
passwordHash String? @db.VarChar(255)
|
|
name String @db.VarChar(255)
|
|
firstName String? @db.VarChar(100)
|
|
lastName String? @db.VarChar(100)
|
|
avatarUrl String? @db.VarChar(500)
|
|
phone String? @db.VarChar(20)
|
|
|
|
status UserStatus @default(ACTIVE)
|
|
lastLoginAt DateTime?
|
|
lastLoginIp String? @db.VarChar(50)
|
|
|
|
// Preferences
|
|
timezone String? @db.VarChar(50)
|
|
language String? @db.VarChar(10)
|
|
preferences Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
userRoles UserRole[]
|
|
userGroups UserGroup[]
|
|
sessions Session[]
|
|
aiActionsTriggered AIAction[] @relation("TriggeredBy")
|
|
aiActionsApproved AIAction[] @relation("ApprovedBy")
|
|
journalEntriesPosted JournalEntry[] @relation("PostedBy")
|
|
journalEntriesApproved JournalEntry[] @relation("ApprovedBy")
|
|
dashboardConfigs DashboardConfig[]
|
|
eveCharacters EveCharacter[]
|
|
|
|
@@unique([organizationId, email])
|
|
@@index([organizationId])
|
|
@@index([email])
|
|
@@index([status])
|
|
@@map("users")
|
|
}
|
|
|
|
model Role {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
roleType RoleType @default(CUSTOM)
|
|
description String? @db.Text
|
|
isSystem Boolean @default(false)
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
permissions Permission[]
|
|
userRoles UserRole[]
|
|
|
|
@@unique([organizationId, name])
|
|
@@index([organizationId])
|
|
@@index([roleType])
|
|
@@map("roles")
|
|
}
|
|
|
|
model Permission {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
roleId String @db.Uuid
|
|
module PermissionModule
|
|
action PermissionAction
|
|
description String? @db.Text
|
|
conditions Json? // Additional conditions (e.g., entity-level restrictions)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([roleId, module, action])
|
|
@@index([organizationId])
|
|
@@index([roleId])
|
|
@@map("permissions")
|
|
}
|
|
|
|
model Group {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
userGroups UserGroup[]
|
|
|
|
@@unique([organizationId, name])
|
|
@@index([organizationId])
|
|
@@map("groups")
|
|
}
|
|
|
|
model UserRole {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @db.Uuid
|
|
roleId String @db.Uuid
|
|
assignedAt DateTime @default(now())
|
|
expiresAt DateTime?
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, roleId])
|
|
@@index([userId])
|
|
@@index([roleId])
|
|
@@map("user_roles")
|
|
}
|
|
|
|
model UserGroup {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @db.Uuid
|
|
groupId String @db.Uuid
|
|
joinedAt DateTime @default(now())
|
|
|
|
// Relations
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([userId, groupId])
|
|
@@index([userId])
|
|
@@index([groupId])
|
|
@@map("user_groups")
|
|
}
|
|
|
|
model Session {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @db.Uuid
|
|
token String @db.VarChar(500)
|
|
expiresAt DateTime
|
|
ipAddress String? @db.VarChar(50)
|
|
userAgent String? @db.Text
|
|
revokedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
// Relations
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([userId])
|
|
@@index([token])
|
|
@@index([expiresAt])
|
|
@@map("sessions")
|
|
}
|
|
|
|
// ============================================================================
|
|
// FINANCIAL CORE - CHART OF ACCOUNTS & JOURNAL
|
|
// ============================================================================
|
|
|
|
model ChartOfAccount {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
accountCode String @db.VarChar(50)
|
|
accountName String @db.VarChar(255)
|
|
accountType AccountType
|
|
parentAccountId String? @db.Uuid
|
|
description String? @db.Text
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
// Account settings
|
|
isControlAccount Boolean @default(false)
|
|
isDetailAccount Boolean @default(true)
|
|
isBankAccount Boolean @default(false)
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
|
|
// Hierarchy
|
|
level Int @default(1)
|
|
hierarchy String @db.VarChar(500) // Denormalized for performance
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
parentAccount ChartOfAccount? @relation("AccountHierarchy", fields: [parentAccountId], references: [id], onDelete: SetNull)
|
|
childAccounts ChartOfAccount[] @relation("AccountHierarchy")
|
|
journalEntryLines JournalEntryLine[]
|
|
budgetLines BudgetLine[]
|
|
|
|
@@unique([organizationId, accountCode])
|
|
@@index([organizationId])
|
|
@@index([accountType])
|
|
@@index([status])
|
|
@@index([parentAccountId])
|
|
@@map("chart_of_accounts")
|
|
}
|
|
|
|
model FiscalPeriod {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
fiscalYear Int
|
|
periodNumber Int // 1-12 or 1-13
|
|
periodName String @db.VarChar(50)
|
|
startDate DateTime
|
|
endDate DateTime
|
|
status FiscalPeriodStatus @default(OPEN)
|
|
closedAt DateTime?
|
|
closedBy String? @db.VarChar(255)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
journalEntries JournalEntry[]
|
|
|
|
@@unique([organizationId, fiscalYear, periodNumber])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@index([startDate, endDate])
|
|
@@map("fiscal_periods")
|
|
}
|
|
|
|
model JournalEntry {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
fiscalPeriodId String @db.Uuid
|
|
entryNumber String @db.VarChar(50)
|
|
entryDate DateTime
|
|
description String @db.Text
|
|
sourceModule String @db.VarChar(100) // AP, AR, PAYROLL, etc.
|
|
referenceId String? @db.VarChar(255) // Links to source document
|
|
|
|
status String @default("DRAFT") @db.VarChar(50)
|
|
totalDebits Decimal @default(0) @db.Decimal(19, 4)
|
|
totalCredits Decimal @default(0) @db.Decimal(19, 4)
|
|
isBalanced Boolean @default(false)
|
|
|
|
postedAt DateTime?
|
|
postedBy String? @db.Uuid
|
|
approvedAt DateTime?
|
|
approvedBy String? @db.Uuid
|
|
|
|
memo String? @db.Text
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
fiscalPeriod FiscalPeriod @relation(fields: [fiscalPeriodId], references: [id], onDelete: Restrict)
|
|
postedByUser User? @relation("PostedBy", fields: [postedBy], references: [id], onDelete: SetNull)
|
|
approvedByUser User? @relation("ApprovedBy", fields: [approvedBy], references: [id], onDelete: SetNull)
|
|
journalEntryLines JournalEntryLine[]
|
|
|
|
@@unique([organizationId, entryNumber])
|
|
@@index([organizationId])
|
|
@@index([entryDate])
|
|
@@index([fiscalPeriodId])
|
|
@@index([status])
|
|
@@index([sourceModule])
|
|
@@map("journal_entries")
|
|
}
|
|
|
|
model JournalEntryLine {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
journalEntryId String @db.Uuid
|
|
entityId String @db.Uuid
|
|
accountId String @db.Uuid
|
|
|
|
debitAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
creditAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
exchangeRate Decimal @default(1) @db.Decimal(19, 8)
|
|
|
|
lineNumber Int
|
|
description String? @db.Text
|
|
memo String? @db.Text
|
|
department String? @db.VarChar(100)
|
|
project String? @db.VarChar(100)
|
|
costCenter String? @db.VarChar(100)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
journalEntry JournalEntry @relation(fields: [journalEntryId], references: [id], onDelete: Cascade)
|
|
entity Entity @relation(fields: [entityId], references: [id], onDelete: Restrict)
|
|
account ChartOfAccount @relation(fields: [accountId], references: [id], onDelete: Restrict)
|
|
|
|
@@index([organizationId])
|
|
@@index([journalEntryId])
|
|
@@index([accountId])
|
|
@@index([entityId])
|
|
@@map("journal_entry_lines")
|
|
}
|
|
|
|
// ============================================================================
|
|
// CURRENCY & EXCHANGE RATES
|
|
// ============================================================================
|
|
|
|
model Currency {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
currencyCode String @db.VarChar(3)
|
|
currencyName String @db.VarChar(100)
|
|
symbolFormat String @db.VarChar(10)
|
|
decimalPlaces Int @default(2)
|
|
isActive Boolean @default(true)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([organizationId, currencyCode])
|
|
@@index([organizationId])
|
|
@@map("currencies")
|
|
}
|
|
|
|
// ============================================================================
|
|
// VENDORS & CUSTOMERS
|
|
// ============================================================================
|
|
|
|
model Vendor {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
vendorCode String @db.VarChar(50)
|
|
vendorName String @db.VarChar(255)
|
|
vendorType String? @db.VarChar(100)
|
|
|
|
// Contact Info
|
|
email String? @db.VarChar(255)
|
|
phone String? @db.VarChar(20)
|
|
website String? @db.VarChar(255)
|
|
|
|
// Address
|
|
street1 String? @db.VarChar(255)
|
|
street2 String? @db.VarChar(255)
|
|
city String? @db.VarChar(100)
|
|
stateProvince String? @db.VarChar(100)
|
|
postalCode String? @db.VarChar(20)
|
|
country String? @db.VarChar(100)
|
|
|
|
// Tax & Legal
|
|
taxId String? @db.VarChar(50)
|
|
vendorTaxId String? @db.VarChar(50)
|
|
|
|
// Payment Terms
|
|
paymentTermDays Int @default(0)
|
|
defaultCurrency String @default("USD") @db.VarChar(3)
|
|
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
creditLimit Decimal? @db.Decimal(19, 4)
|
|
isOnHold Boolean @default(false)
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
invoices Invoice[]
|
|
payments Payment[]
|
|
purchaseOrders PurchaseOrder[]
|
|
|
|
@@unique([organizationId, vendorCode])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@map("vendors")
|
|
}
|
|
|
|
model Customer {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
customerCode String @db.VarChar(50)
|
|
customerName String @db.VarChar(255)
|
|
customerType String? @db.VarChar(100)
|
|
|
|
// Contact Info
|
|
email String? @db.VarChar(255)
|
|
phone String? @db.VarChar(20)
|
|
website String? @db.VarChar(255)
|
|
contactPerson String? @db.VarChar(255)
|
|
|
|
// Address
|
|
street1 String? @db.VarChar(255)
|
|
street2 String? @db.VarChar(255)
|
|
city String? @db.VarChar(100)
|
|
stateProvince String? @db.VarChar(100)
|
|
postalCode String? @db.VarChar(20)
|
|
country String? @db.VarChar(100)
|
|
|
|
// Tax & Legal
|
|
taxId String? @db.VarChar(50)
|
|
|
|
// Business Terms
|
|
paymentTermDays Int @default(0)
|
|
defaultCurrency String @default("USD") @db.VarChar(3)
|
|
creditLimit Decimal? @db.Decimal(19, 4)
|
|
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
isOnCredit Boolean @default(false)
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
invoices Invoice[]
|
|
payments Payment[]
|
|
subscriptions Subscription[]
|
|
revenueContracts RevenueContract[]
|
|
|
|
@@unique([organizationId, customerCode])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@map("customers")
|
|
}
|
|
|
|
// ============================================================================
|
|
// INVOICES
|
|
// ============================================================================
|
|
|
|
model Invoice {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
entityId String @db.Uuid
|
|
invoiceNumber String @db.VarChar(50)
|
|
invoiceType InvoiceType
|
|
|
|
// Party Info
|
|
vendorId String? @db.Uuid
|
|
customerId String? @db.Uuid
|
|
|
|
invoiceDate DateTime
|
|
dueDate DateTime?
|
|
expectedPaymentDate DateTime?
|
|
|
|
// Amount
|
|
subtotalAmount Decimal @db.Decimal(19, 4)
|
|
taxAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
totalAmount Decimal @db.Decimal(19, 4)
|
|
paidAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
balanceDue Decimal @db.Decimal(19, 4)
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
exchangeRate Decimal @default(1) @db.Decimal(19, 8)
|
|
|
|
// Details
|
|
description String? @db.Text
|
|
poNumber String? @db.VarChar(100)
|
|
referenceNumber String? @db.VarChar(100)
|
|
|
|
status InvoiceStatus @default(DRAFT)
|
|
approvedAt DateTime?
|
|
approvedBy String? @db.VarChar(255)
|
|
cancelledAt DateTime?
|
|
cancelledBy String? @db.VarChar(255)
|
|
cancelReason String? @db.Text
|
|
|
|
daysOverdue Int?
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
entity Entity @relation(fields: [entityId], references: [id], onDelete: Restrict)
|
|
vendor Vendor? @relation(fields: [vendorId], references: [id], onDelete: SetNull)
|
|
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
|
|
invoiceLines InvoiceLine[]
|
|
payments Payment[]
|
|
paymentAllocations PaymentAllocation[]
|
|
|
|
@@unique([organizationId, invoiceNumber])
|
|
@@index([organizationId])
|
|
@@index([invoiceType])
|
|
@@index([status])
|
|
@@index([invoiceDate])
|
|
@@index([dueDate])
|
|
@@index([vendorId])
|
|
@@index([customerId])
|
|
@@map("invoices")
|
|
}
|
|
|
|
model InvoiceLine {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
invoiceId String @db.Uuid
|
|
lineNumber Int
|
|
description String @db.Text
|
|
quantity Decimal @db.Decimal(19, 4)
|
|
unitPrice Decimal @db.Decimal(19, 4)
|
|
lineAmount Decimal @db.Decimal(19, 4)
|
|
|
|
glAccountCode String? @db.VarChar(50)
|
|
department String? @db.VarChar(100)
|
|
project String? @db.VarChar(100)
|
|
taxCode String? @db.VarChar(50)
|
|
taxAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([invoiceId])
|
|
@@map("invoice_lines")
|
|
}
|
|
|
|
// ============================================================================
|
|
// PAYMENTS
|
|
// ============================================================================
|
|
|
|
model Payment {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
paymentNumber String @db.VarChar(50)
|
|
|
|
// Party & Invoice Info
|
|
vendorId String? @db.Uuid
|
|
customerId String? @db.Uuid
|
|
invoiceId String? @db.Uuid
|
|
|
|
// Payment Details
|
|
paymentDate DateTime
|
|
paymentMethod PaymentMethod
|
|
paymentReference String? @db.VarChar(255)
|
|
|
|
// Account Info
|
|
bankAccountId String? @db.Uuid
|
|
checkNumber String? @db.VarChar(50)
|
|
|
|
// Amount
|
|
paymentAmount Decimal @db.Decimal(19, 4)
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
exchangeRate Decimal @default(1) @db.Decimal(19, 8)
|
|
feeAmount Decimal @default(0) @db.Decimal(19, 4)
|
|
totalPaymentAmount Decimal @db.Decimal(19, 4)
|
|
|
|
// Status
|
|
status PaymentStatus @default(DRAFT)
|
|
approvedAt DateTime?
|
|
approvedBy String? @db.VarChar(255)
|
|
processedAt DateTime?
|
|
processedBy String? @db.VarChar(255)
|
|
failureReason String? @db.Text
|
|
|
|
// Metadata
|
|
memo String? @db.Text
|
|
|
|
// Audit
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
vendor Vendor? @relation(fields: [vendorId], references: [id], onDelete: SetNull)
|
|
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
|
|
invoice Invoice? @relation(fields: [invoiceId], references: [id], onDelete: SetNull)
|
|
bankAccount BankAccount? @relation(fields: [bankAccountId], references: [id], onDelete: SetNull)
|
|
paymentAllocations PaymentAllocation[]
|
|
|
|
@@unique([organizationId, paymentNumber])
|
|
@@index([organizationId])
|
|
@@index([paymentDate])
|
|
@@index([status])
|
|
@@index([vendorId])
|
|
@@index([customerId])
|
|
@@map("payments")
|
|
}
|
|
|
|
model PaymentAllocation {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
paymentId String @db.Uuid
|
|
invoiceId String @db.Uuid
|
|
|
|
allocatedAmount Decimal @db.Decimal(19, 4)
|
|
allocationDate DateTime @default(now())
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
payment Payment @relation(fields: [paymentId], references: [id], onDelete: Cascade)
|
|
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([paymentId, invoiceId])
|
|
@@index([organizationId])
|
|
@@index([paymentId])
|
|
@@index([invoiceId])
|
|
@@map("payment_allocations")
|
|
}
|
|
|
|
// ============================================================================
|
|
// TREASURY & BANK ACCOUNTS
|
|
// ============================================================================
|
|
|
|
model BankAccount {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
entityId String @db.Uuid
|
|
accountName String @db.VarChar(255)
|
|
bankName String @db.VarChar(255)
|
|
accountNumber String @db.VarChar(50) // Typically masked
|
|
accountNumberMasked String @db.VarChar(50)
|
|
routingNumber String? @db.VarChar(20)
|
|
swiftCode String? @db.VarChar(20)
|
|
ibanCode String? @db.VarChar(50)
|
|
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
currentBalance Decimal @db.Decimal(19, 4)
|
|
availableBalance Decimal? @db.Decimal(19, 4)
|
|
lastBalanceUpdate DateTime?
|
|
|
|
accountType String @db.VarChar(50) // Checking, Savings, etc.
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
// Integration
|
|
connectorId String? @db.Uuid
|
|
externalAccountId String? @db.VarChar(255)
|
|
lastSyncAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
entity Entity @relation(fields: [entityId], references: [id], onDelete: Restrict)
|
|
connector Connector? @relation(fields: [connectorId], references: [id], onDelete: SetNull)
|
|
bankTransactions BankTransaction[]
|
|
payments Payment[]
|
|
|
|
@@unique([organizationId, accountNumber])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@map("bank_accounts")
|
|
}
|
|
|
|
model BankTransaction {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
bankAccountId String @db.Uuid
|
|
|
|
transactionDate DateTime
|
|
postDate DateTime?
|
|
transactionType TransactionType
|
|
|
|
amount Decimal @db.Decimal(19, 4)
|
|
description String @db.Text
|
|
|
|
// Matching
|
|
invoiceId String? @db.Uuid
|
|
paymentId String? @db.Uuid
|
|
journalEntryId String? @db.Uuid
|
|
reconciled Boolean @default(false)
|
|
reconciledAt DateTime?
|
|
|
|
// Bank-provided fields
|
|
checkNumber String? @db.VarChar(50)
|
|
referenceNumber String? @db.VarChar(255)
|
|
oppositeParty String? @db.VarChar(255)
|
|
|
|
status String @default("PENDING") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
bankAccount BankAccount @relation(fields: [bankAccountId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([bankAccountId])
|
|
@@index([transactionDate])
|
|
@@index([reconciled])
|
|
@@map("bank_transactions")
|
|
}
|
|
|
|
model BankReconciliation {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
bankAccountId String @db.Uuid
|
|
|
|
// Period covered
|
|
periodStart DateTime
|
|
periodEnd DateTime
|
|
statementDate DateTime
|
|
statementBalance Decimal @db.Decimal(19, 4)
|
|
|
|
// Computed at completion
|
|
beginningBalance Decimal @default(0) @db.Decimal(19, 4)
|
|
endingBookBalance Decimal @default(0) @db.Decimal(19, 4)
|
|
clearedDeposits Decimal @default(0) @db.Decimal(19, 4)
|
|
clearedWithdrawals Decimal @default(0) @db.Decimal(19, 4)
|
|
outstandingDeposits Decimal @default(0) @db.Decimal(19, 4)
|
|
outstandingWithdrawals Decimal @default(0) @db.Decimal(19, 4)
|
|
difference Decimal @default(0) @db.Decimal(19, 4)
|
|
|
|
status String @default("DRAFT") @db.VarChar(20)
|
|
// DRAFT | COMPLETED | REOPENED
|
|
|
|
reconciledTransactionCount Int @default(0)
|
|
notes String? @db.Text
|
|
|
|
completedAt DateTime?
|
|
completedBy String? @db.VarChar(255)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([bankAccountId])
|
|
@@index([statementDate])
|
|
@@map("bank_reconciliations")
|
|
}
|
|
|
|
model CashPosition {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
snapshotDate DateTime
|
|
totalCash Decimal @db.Decimal(19, 4)
|
|
totalAvailable Decimal @db.Decimal(19, 4)
|
|
|
|
// Breakdown
|
|
cashByEntity Json? // Map of entity -> cash amount
|
|
cashByCurrency Json? // Map of currency -> cash amount
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([organizationId])
|
|
@@index([snapshotDate])
|
|
@@map("cash_positions")
|
|
}
|
|
|
|
// ============================================================================
|
|
// INTEGRATIONS
|
|
// ============================================================================
|
|
|
|
model Connector {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
name String @db.VarChar(255)
|
|
type ConnectorType
|
|
provider String @db.VarChar(100)
|
|
description String? @db.Text
|
|
|
|
status ConnectorStatus @default(INACTIVE)
|
|
healthStatus ConnectorHealthStatus @default(UNKNOWN)
|
|
|
|
// Configuration
|
|
config Json? // Encrypted config with credentials
|
|
configVersion Int @default(1)
|
|
|
|
// Sync tracking
|
|
lastSyncAt DateTime?
|
|
lastSyncStatus String? @db.VarChar(50)
|
|
lastErrorMessage String? @db.Text
|
|
nextScheduledSync DateTime?
|
|
syncFrequencyMinutes Int @default(60)
|
|
|
|
// Features
|
|
isBidirectional Boolean @default(false)
|
|
supportsRealtime Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
connectorLogs ConnectorLog[]
|
|
bankAccounts BankAccount[]
|
|
|
|
@@unique([organizationId, name])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@index([type])
|
|
@@map("connectors")
|
|
}
|
|
|
|
model ConnectorLog {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
connectorId String @db.Uuid
|
|
|
|
syncStartTime DateTime
|
|
syncEndTime DateTime?
|
|
duration Int? // milliseconds
|
|
|
|
status String @db.VarChar(50) // SUCCESS, PARTIAL, FAILED
|
|
recordsSynced Int @default(0)
|
|
recordsProcessed Int @default(0)
|
|
recordsFailed Int @default(0)
|
|
|
|
errorMessage String? @db.Text
|
|
errorDetails Json?
|
|
|
|
// AI Healing
|
|
aiHealingAttempted Boolean @default(false)
|
|
aiHealingAction String? @db.Text
|
|
aiHealingSuccess Boolean @default(false)
|
|
|
|
// Metadata
|
|
syncType String @db.VarChar(50) // FULL, INCREMENTAL
|
|
metadata Json?
|
|
|
|
// Relations
|
|
connector Connector @relation(fields: [connectorId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([connectorId])
|
|
@@index([syncStartTime])
|
|
@@index([status])
|
|
@@map("connector_logs")
|
|
}
|
|
|
|
// ============================================================================
|
|
// AI AUDIT TRAIL
|
|
// ============================================================================
|
|
|
|
model AIAction {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
actionType AIActionType
|
|
module String @db.VarChar(100)
|
|
|
|
inputSummary String @db.Text
|
|
outputSummary String @db.Text
|
|
|
|
modelUsed String @db.VarChar(100)
|
|
modelVersion String? @db.VarChar(50)
|
|
|
|
confidence Decimal? @db.Decimal(5, 4) // 0.0-1.0
|
|
|
|
// User tracking
|
|
triggeredByUserId String @db.Uuid
|
|
approvedByUserId String? @db.Uuid
|
|
|
|
status AIActionStatus @default(PENDING)
|
|
|
|
// Approval tracking
|
|
approvalComment String? @db.Text
|
|
approvalDate DateTime?
|
|
rejectionReason String? @db.Text
|
|
rejectionDate DateTime?
|
|
|
|
// Related records
|
|
relatedEntityType String? @db.VarChar(100) // Journal, Invoice, Payment, etc.
|
|
relatedEntityId String? @db.Uuid
|
|
|
|
// Metadata
|
|
parameters Json?
|
|
result Json?
|
|
|
|
executedAt DateTime?
|
|
duration Int? // milliseconds
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
triggeredBy User @relation("TriggeredBy", fields: [triggeredByUserId], references: [id], onDelete: Restrict)
|
|
approvedBy User? @relation("ApprovedBy", fields: [approvedByUserId], references: [id], onDelete: SetNull)
|
|
|
|
@@index([organizationId])
|
|
@@index([actionType])
|
|
@@index([module])
|
|
@@index([status])
|
|
@@index([createdAt])
|
|
@@index([triggeredByUserId])
|
|
@@map("ai_actions")
|
|
}
|
|
|
|
// ============================================================================
|
|
// DASHBOARD
|
|
// ============================================================================
|
|
|
|
model DashboardConfig {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
userId String @db.Uuid
|
|
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
isDefault Boolean @default(false)
|
|
|
|
layoutConfig Json // Dashboard layout and widget arrangement
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
widgets Widget[]
|
|
|
|
@@unique([organizationId, userId, isDefault])
|
|
@@index([organizationId])
|
|
@@index([userId])
|
|
@@map("dashboard_configs")
|
|
}
|
|
|
|
model Widget {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
dashboardConfigId String @db.Uuid
|
|
|
|
widgetType String @db.VarChar(100)
|
|
title String @db.VarChar(255)
|
|
description String? @db.Text
|
|
|
|
position Int // Position in grid
|
|
rows Int @default(1)
|
|
cols Int @default(1)
|
|
|
|
config Json? // Widget-specific config
|
|
dataSource String? @db.VarChar(255)
|
|
refreshInterval Int? // seconds
|
|
|
|
isVisible Boolean @default(true)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
dashboardConfig DashboardConfig @relation(fields: [dashboardConfigId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([dashboardConfigId])
|
|
@@map("widgets")
|
|
}
|
|
|
|
// ─── Audit Trail ─────────────────────────────────────────────────
|
|
|
|
model AuditLog {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
userId String? @db.Uuid
|
|
userName String? @db.VarChar(255)
|
|
|
|
action String @db.VarChar(50) // CREATE, UPDATE, DELETE, APPROVE, REJECT, POST, VOID, LOGIN, EXPORT
|
|
module String @db.VarChar(100) // general-ledger, ap, ar, treasury, etc.
|
|
entityType String @db.VarChar(100) // JournalEntry, Invoice, Payment, etc.
|
|
entityId String? @db.VarChar(255)
|
|
entityLabel String? @db.VarChar(500) // Human-readable label (e.g., "JE-2024-001")
|
|
|
|
description String @db.Text // "Created journal entry JE-2024-001"
|
|
changes Json? // { field: { old: x, new: y } }
|
|
metadata Json? // Extra context
|
|
|
|
ipAddress String? @db.VarChar(50)
|
|
userAgent String? @db.Text
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
// Relations
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId, createdAt])
|
|
@@index([organizationId, module])
|
|
@@index([organizationId, entityType, entityId])
|
|
@@index([userId])
|
|
@@map("audit_logs")
|
|
}
|
|
|
|
// ─── Cap Table ───────────────────────────────────────────────────
|
|
|
|
model Shareholder {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
type String @db.VarChar(50) // founder, employee, investor, advisor
|
|
sharesOwned Int
|
|
shareClass String @db.VarChar(50) // Common, Series A, Series B, Options
|
|
vestingCliffMonths Int @default(0)
|
|
vestingTotalMonths Int @default(0)
|
|
vestedPercent Decimal @default(0) @db.Decimal(5,2)
|
|
grantDate DateTime
|
|
exercisePrice Decimal @default(0) @db.Decimal(19,4)
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@map("shareholders")
|
|
}
|
|
|
|
// ─── Real Estate & Leases ────────────────────────────────────────
|
|
|
|
model Lease {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
description String @db.VarChar(500)
|
|
type String @db.VarChar(50) // Operating, Finance
|
|
startDate DateTime
|
|
endDate DateTime
|
|
monthlyPayment Decimal @db.Decimal(19,4)
|
|
totalCommitment Decimal @db.Decimal(19,4)
|
|
discountRate Decimal @db.Decimal(5,2)
|
|
rouAsset Decimal @default(0) @db.Decimal(19,4)
|
|
leaseLiability Decimal @default(0) @db.Decimal(19,4)
|
|
currentPortion Decimal @default(0) @db.Decimal(19,4)
|
|
nonCurrentPortion Decimal @default(0) @db.Decimal(19,4)
|
|
status String @default("Active") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@map("leases")
|
|
}
|
|
|
|
// ─── Budget ──────────────────────────────────────────────────────
|
|
|
|
model BudgetLine {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
accountId String @db.Uuid
|
|
fiscalYear Int
|
|
monthlyAmounts Json // Array of 12 monthly budget amounts
|
|
annualBudget Decimal @db.Decimal(19,4)
|
|
notes String? @db.Text
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
account ChartOfAccount @relation(fields: [accountId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([organizationId, accountId, fiscalYear])
|
|
@@index([organizationId])
|
|
@@index([fiscalYear])
|
|
@@map("budget_lines")
|
|
}
|
|
|
|
// ─── Procurement ─────────────────────────────────────────────────
|
|
|
|
model PurchaseOrder {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
poNumber String @db.VarChar(50)
|
|
vendorId String @db.Uuid
|
|
orderDate DateTime
|
|
expectedDate DateTime?
|
|
status String @default("DRAFT") @db.VarChar(50) // DRAFT, SUBMITTED, APPROVED, RECEIVED, CANCELLED
|
|
subtotal Decimal @default(0) @db.Decimal(19,4)
|
|
taxAmount Decimal @default(0) @db.Decimal(19,4)
|
|
totalAmount Decimal @default(0) @db.Decimal(19,4)
|
|
notes String? @db.Text
|
|
approvedBy String? @db.VarChar(255)
|
|
approvedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
vendor Vendor @relation(fields: [vendorId], references: [id], onDelete: Restrict)
|
|
lines PurchaseOrderLine[]
|
|
|
|
@@unique([organizationId, poNumber])
|
|
@@index([organizationId])
|
|
@@index([vendorId])
|
|
@@index([status])
|
|
@@map("purchase_orders")
|
|
}
|
|
|
|
model PurchaseOrderLine {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
purchaseOrderId String @db.Uuid
|
|
lineNumber Int
|
|
description String @db.Text
|
|
quantity Decimal @db.Decimal(19,4)
|
|
unitPrice Decimal @db.Decimal(19,4)
|
|
lineAmount Decimal @db.Decimal(19,4)
|
|
glAccountCode String? @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
purchaseOrder PurchaseOrder @relation(fields: [purchaseOrderId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([purchaseOrderId])
|
|
@@map("purchase_order_lines")
|
|
}
|
|
|
|
// ─── Documents ───────────────────────────────────────────────────
|
|
|
|
model Document {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
category String @db.VarChar(100) // contract, invoice, receipt, report, legal, other
|
|
fileSize Int @default(0) // bytes
|
|
mimeType String @db.VarChar(100)
|
|
storagePath String @db.VarChar(500) // local path or URL
|
|
status String @default("ACTIVE") @db.VarChar(50)
|
|
tags Json? // array of tags
|
|
uploadedBy String? @db.VarChar(255)
|
|
|
|
// Link to related entity
|
|
relatedEntityType String? @db.VarChar(100)
|
|
relatedEntityId String? @db.Uuid
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([category])
|
|
@@index([status])
|
|
@@map("documents")
|
|
}
|
|
|
|
// ─── Billing & Subscriptions ─────────────────────────────────────
|
|
|
|
model Subscription {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
customerId String @db.Uuid
|
|
planName String @db.VarChar(255)
|
|
status String @default("ACTIVE") @db.VarChar(50) // ACTIVE, PAUSED, CANCELLED, EXPIRED
|
|
billingCycle String @db.VarChar(50) // MONTHLY, QUARTERLY, ANNUAL
|
|
amount Decimal @db.Decimal(19,4)
|
|
currencyCode String @default("USD") @db.VarChar(3)
|
|
startDate DateTime
|
|
endDate DateTime?
|
|
nextBillingDate DateTime?
|
|
cancelledAt DateTime?
|
|
notes String? @db.Text
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
customer Customer @relation(fields: [customerId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([customerId])
|
|
@@index([status])
|
|
@@map("subscriptions")
|
|
}
|
|
|
|
// ─── Financial Planning & Forecasting ────────────────────────────
|
|
|
|
model Forecast {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
scenarioType String @db.VarChar(50) // BASE, OPTIMISTIC, PESSIMISTIC, CUSTOM
|
|
startDate DateTime
|
|
endDate DateTime
|
|
assumptions Json? // revenue growth rate, expense factors, etc.
|
|
projections Json? // monthly projections data
|
|
status String @default("DRAFT") @db.VarChar(50)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([scenarioType])
|
|
@@map("forecasts")
|
|
}
|
|
|
|
// ─── Driver-Based Planning ───────────────────────────────────────
|
|
|
|
model PlanningDriver {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
key String @db.VarChar(100) // stable machine key e.g. "revenue_growth"
|
|
name String @db.VarChar(255)
|
|
category String @db.VarChar(50) // REVENUE | COST | HEADCOUNT | PRICING | CHURN | UNIT | OTHER
|
|
unit String @db.VarChar(50) // PERCENT | CURRENCY | COUNT | RATIO
|
|
value Decimal @db.Decimal(19,6) // current/base value
|
|
minValue Decimal? @db.Decimal(19,6)
|
|
maxValue Decimal? @db.Decimal(19,6)
|
|
description String? @db.Text
|
|
formula String? @db.Text // optional expression for derived drivers
|
|
tags Json? // array of string tags
|
|
isActive Boolean @default(true)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([organizationId, key])
|
|
@@index([organizationId])
|
|
@@index([category])
|
|
@@map("planning_drivers")
|
|
}
|
|
|
|
model PlanningScenario {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
scenarioType String @default("CUSTOM") @db.VarChar(50) // BASE | OPTIMISTIC | PESSIMISTIC | CUSTOM
|
|
horizon Int @default(12) // months
|
|
driverOverrides Json // { [driverKey]: number }
|
|
assumptions Json? // free-form notes / flags
|
|
results Json? // cached computed output
|
|
isBaseline Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([scenarioType])
|
|
@@index([isBaseline])
|
|
@@map("planning_scenarios")
|
|
}
|
|
|
|
// ─── Month-End Close ─────────────────────────────────────────────
|
|
|
|
model ClosePeriod {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
year Int
|
|
month Int // 1-12
|
|
periodLabel String @db.VarChar(50) // e.g. "Mar 2026"
|
|
status String @default("OPEN") @db.VarChar(20) // OPEN | IN_PROGRESS | REVIEW | CLOSED
|
|
targetCloseDate DateTime?
|
|
startedAt DateTime?
|
|
actualCloseDate DateTime?
|
|
closedBy String? @db.VarChar(255)
|
|
notes String? @db.Text
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
tasks CloseTask[]
|
|
|
|
@@unique([organizationId, year, month])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@map("close_periods")
|
|
}
|
|
|
|
model CloseTask {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
closePeriodId String? @db.Uuid // null = template task
|
|
taskKey String @db.VarChar(100) // stable key for template lookup
|
|
order Int @default(0)
|
|
title String @db.VarChar(255)
|
|
description String? @db.Text
|
|
category String @db.VarChar(50) // AP | AR | BANK | PAYROLL | FIXED_ASSETS | REVENUE | PREPAID | INTERCO | RECON | REVIEW | REPORTING | LOCK
|
|
owner String? @db.VarChar(255)
|
|
status String @default("PENDING") @db.VarChar(20) // PENDING | IN_PROGRESS | BLOCKED | REVIEW | DONE | SKIPPED
|
|
dependsOn Json? // array of taskKey strings
|
|
estimatedMinutes Int?
|
|
actualMinutes Int?
|
|
completedAt DateTime?
|
|
completedBy String? @db.VarChar(255)
|
|
dueDate DateTime?
|
|
notes String? @db.Text
|
|
automatable Boolean @default(false)
|
|
aiAssisted Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
closePeriod ClosePeriod? @relation(fields: [closePeriodId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([closePeriodId])
|
|
@@index([status])
|
|
@@index([category])
|
|
@@map("close_tasks")
|
|
}
|
|
|
|
// ─── Revenue Recognition (ASC 606) ───────────────────────────────
|
|
|
|
model RevenueContract {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
customerId String? @db.Uuid
|
|
contractNumber String @db.VarChar(100)
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
|
|
// Performance obligation
|
|
totalValue Decimal @db.Decimal(19,2)
|
|
currency String @default("USD") @db.VarChar(3)
|
|
|
|
// Service period
|
|
startDate DateTime
|
|
endDate DateTime
|
|
|
|
// Recognition method
|
|
recognitionMethod String @default("STRAIGHT_LINE") @db.VarChar(30)
|
|
// STRAIGHT_LINE | POINT_IN_TIME | MILESTONE | USAGE_BASED
|
|
|
|
billingFrequency String @default("MONTHLY") @db.VarChar(20)
|
|
// ONE_TIME | MONTHLY | QUARTERLY | ANNUAL
|
|
|
|
status String @default("ACTIVE") @db.VarChar(20)
|
|
// DRAFT | ACTIVE | COMPLETED | CANCELLED
|
|
|
|
// GL accounts for posting
|
|
deferredAccountCode String? @db.VarChar(50) // liability account
|
|
revenueAccountCode String? @db.VarChar(50) // P&L revenue account
|
|
|
|
notes String? @db.Text
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
customer Customer? @relation(fields: [customerId], references: [id], onDelete: SetNull)
|
|
scheduleLines RevenueScheduleLine[]
|
|
|
|
@@unique([organizationId, contractNumber])
|
|
@@index([organizationId])
|
|
@@index([customerId])
|
|
@@index([status])
|
|
@@map("revenue_contracts")
|
|
}
|
|
|
|
model RevenueScheduleLine {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
contractId String @db.Uuid
|
|
|
|
periodYear Int
|
|
periodMonth Int // 1-12
|
|
periodLabel String @db.VarChar(50) // "Mar 2026"
|
|
|
|
amount Decimal @db.Decimal(19,2)
|
|
cumulativeRecognized Decimal? @db.Decimal(19,2)
|
|
cumulativeDeferred Decimal? @db.Decimal(19,2)
|
|
|
|
status String @default("SCHEDULED") @db.VarChar(20)
|
|
// SCHEDULED | RECOGNIZED | POSTED
|
|
|
|
recognizedAt DateTime?
|
|
postedAt DateTime?
|
|
journalEntryId String? @db.Uuid
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
contract RevenueContract @relation(fields: [contractId], references: [id], onDelete: Cascade)
|
|
|
|
@@index([organizationId])
|
|
@@index([contractId])
|
|
@@index([organizationId, periodYear, periodMonth])
|
|
@@index([status])
|
|
@@map("revenue_schedule_lines")
|
|
}
|
|
|
|
// ─── Fixed Assets Register ───────────────────────────────────────
|
|
|
|
model FixedAsset {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
// Identification
|
|
assetTag String @db.VarChar(100)
|
|
name String @db.VarChar(255)
|
|
description String? @db.Text
|
|
category String @db.VarChar(100) // Equipment | Furniture | Vehicles | Software | Leasehold | Buildings | Land | Intangible
|
|
serialNumber String? @db.VarChar(100)
|
|
location String? @db.VarChar(255)
|
|
custodian String? @db.VarChar(255)
|
|
|
|
// Optional GL link (asset & accumulated depreciation accounts)
|
|
assetAccountCode String? @db.VarChar(50)
|
|
accumDepreciationAccountCode String? @db.VarChar(50)
|
|
depreciationExpenseAccountCode String? @db.VarChar(50)
|
|
|
|
// Financials
|
|
acquisitionDate DateTime
|
|
originalCost Decimal @db.Decimal(19,2)
|
|
salvageValue Decimal @default(0) @db.Decimal(19,2)
|
|
usefulLifeYears Int
|
|
depreciationMethod String @default("STRAIGHT_LINE") @db.VarChar(30)
|
|
// STRAIGHT_LINE | DOUBLE_DECLINING | UNITS_OF_PRODUCTION
|
|
|
|
// Tracking
|
|
accumulatedDepreciation Decimal @default(0) @db.Decimal(19,2)
|
|
lastDepreciationDate DateTime?
|
|
|
|
// Status
|
|
status String @default("ACTIVE") @db.VarChar(20)
|
|
// ACTIVE | DISPOSED | FULLY_DEPRECIATED | IDLE
|
|
disposalDate DateTime?
|
|
disposalProceeds Decimal? @db.Decimal(19,2)
|
|
disposalNotes String? @db.Text
|
|
|
|
metadata Json?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
createdBy String? @db.VarChar(255)
|
|
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([organizationId, assetTag])
|
|
@@index([organizationId])
|
|
@@index([category])
|
|
@@index([status])
|
|
@@map("fixed_assets")
|
|
}
|
|
|
|
|
|
// ============================================================================
|
|
// EVE ONLINE SSO & ESI INTEGRATION
|
|
// ============================================================================
|
|
|
|
enum EsiSyncStatus {
|
|
IDLE
|
|
RUNNING
|
|
COMPLETED
|
|
FAILED
|
|
}
|
|
|
|
/// Stores EVE SSO OAuth2 tokens for authenticated characters.
|
|
/// Each ERP user can link one or more EVE characters.
|
|
model EveCharacter {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
userId String @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
// EVE character identity (from JWT sub claim)
|
|
characterId Int @unique
|
|
characterName String @db.VarChar(255)
|
|
characterOwnerHash String @db.VarChar(255)
|
|
|
|
// Corporation affiliation
|
|
corporationId Int?
|
|
corporationName String? @db.VarChar(255)
|
|
allianceId Int?
|
|
allianceName String? @db.VarChar(255)
|
|
|
|
// OAuth2 tokens (encrypted at rest in production)
|
|
accessToken String @db.Text
|
|
refreshToken String @db.Text
|
|
tokenExpiresAt DateTime
|
|
scopes String @db.Text // Space-separated granted scopes
|
|
|
|
// Status
|
|
isActive Boolean @default(true)
|
|
isPrimary Boolean @default(false) // Primary character for this user
|
|
lastSyncAt DateTime?
|
|
tokenError String? @db.Text // Last token refresh error, if any
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
|
|
syncJobs EsiSyncJob[]
|
|
|
|
@@index([userId])
|
|
@@index([organizationId])
|
|
@@index([corporationId])
|
|
@@map("eve_characters")
|
|
}
|
|
|
|
/// Tracks ESI data sync operations per endpoint.
|
|
/// Used for rate limiting, caching (ETag/Expires), and error tracking.
|
|
model EsiSyncJob {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
eveCharacterId String @db.Uuid
|
|
organizationId String @db.Uuid
|
|
|
|
// What we're syncing
|
|
endpoint String @db.VarChar(255) // e.g. "corporations/{id}/wallets"
|
|
corporationId Int?
|
|
division Int? // Wallet division (1-7)
|
|
|
|
// Sync state
|
|
status EsiSyncStatus @default(IDLE)
|
|
lastSuccessAt DateTime?
|
|
lastFailureAt DateTime?
|
|
lastErrorMessage String? @db.Text
|
|
consecutiveFailures Int @default(0)
|
|
|
|
// ESI cache headers
|
|
etag String? @db.VarChar(255)
|
|
cachedUntil DateTime? // From Expires header
|
|
pagesFetched Int @default(0) // For paginated endpoints
|
|
totalPages Int?
|
|
|
|
// Stats
|
|
recordsSynced Int @default(0)
|
|
lastRunDurationMs Int?
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
// Relations
|
|
eveCharacter EveCharacter @relation(fields: [eveCharacterId], references: [id], onDelete: Cascade)
|
|
|
|
@@unique([eveCharacterId, endpoint, division])
|
|
@@index([organizationId])
|
|
@@index([status])
|
|
@@index([cachedUntil])
|
|
@@map("esi_sync_jobs")
|
|
}
|
|
|
|
/// Cached ESI wallet journal entries before they're mapped to GL journal entries.
|
|
/// Raw data from /corporations/{id}/wallets/{division}/journal/
|
|
model EsiWalletJournal {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
division Int
|
|
|
|
// ESI fields
|
|
esiId BigInt // ESI journal ref_id
|
|
date DateTime
|
|
refType String @db.VarChar(100) // e.g. "market_transaction", "contract_price"
|
|
firstPartyId Int?
|
|
firstPartyType String? @db.VarChar(50) // character, corporation, etc.
|
|
secondPartyId Int?
|
|
secondPartyType String? @db.VarChar(50)
|
|
amount Decimal @db.Decimal(19, 4) // ISK amount (positive or negative)
|
|
balance Decimal @db.Decimal(19, 4) // Running balance after entry
|
|
reason String? @db.Text
|
|
description String? @db.Text
|
|
taxAmount Decimal? @db.Decimal(19, 4)
|
|
taxReceiverId Int?
|
|
contextId BigInt?
|
|
contextIdType String? @db.VarChar(50)
|
|
|
|
// ERP mapping
|
|
journalEntryId String? @db.Uuid // Link to GL JournalEntry once mapped
|
|
isMapped Boolean @default(false)
|
|
mappedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([corporationId, division, esiId])
|
|
@@index([organizationId])
|
|
@@index([corporationId, division, date])
|
|
@@index([isMapped])
|
|
@@index([refType])
|
|
@@map("esi_wallet_journal")
|
|
}
|
|
|
|
/// Cached ESI wallet transactions (market buys/sells).
|
|
/// From /corporations/{id}/wallets/{division}/transactions/
|
|
model EsiWalletTransaction {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
division Int
|
|
|
|
// ESI fields
|
|
transactionId BigInt
|
|
date DateTime
|
|
typeId Int // EVE type ID (item)
|
|
typeName String? @db.VarChar(255)
|
|
quantity Int
|
|
unitPrice Decimal @db.Decimal(19, 4)
|
|
clientId Int // Buyer/seller character/corp
|
|
clientName String? @db.VarChar(255)
|
|
locationId BigInt // Station/structure ID
|
|
locationName String? @db.VarChar(255)
|
|
isBuy Boolean // true = purchase, false = sale
|
|
journalRefId BigInt // Links to wallet journal entry
|
|
|
|
// ERP mapping
|
|
invoiceId String? @db.Uuid // Link to AR/AP invoice once mapped
|
|
isMapped Boolean @default(false)
|
|
mappedAt DateTime?
|
|
|
|
createdAt DateTime @default(now())
|
|
|
|
@@unique([corporationId, division, transactionId])
|
|
@@index([organizationId])
|
|
@@index([corporationId, division, date])
|
|
@@index([isMapped])
|
|
@@index([typeId])
|
|
@@map("esi_wallet_transactions")
|
|
}
|
|
|
|
/// Cached ESI market orders.
|
|
/// Sell orders → Accounts Receivable, Buy orders → Accounts Payable
|
|
model EsiMarketOrder {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
|
|
// ESI fields
|
|
orderId BigInt @unique
|
|
typeId Int
|
|
typeName String? @db.VarChar(255)
|
|
locationId BigInt
|
|
locationName String? @db.VarChar(255)
|
|
regionId Int
|
|
price Decimal @db.Decimal(19, 4)
|
|
volumeTotal Int
|
|
volumeRemain Int
|
|
isBuyOrder Boolean
|
|
issued DateTime
|
|
duration Int // Days
|
|
minVolume Int @default(1)
|
|
state String @db.VarChar(50) // active, cancelled, expired, fulfilled
|
|
escrow Decimal? @db.Decimal(19, 4) // For buy orders
|
|
walletDivision Int
|
|
|
|
// ERP mapping
|
|
invoiceId String? @db.Uuid
|
|
isMapped Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([organizationId])
|
|
@@index([corporationId, state])
|
|
@@index([typeId])
|
|
@@map("esi_market_orders")
|
|
}
|
|
|
|
/// Cached ESI industry jobs.
|
|
/// Maps to Project Accounting for cost tracking per manufacturing run.
|
|
model EsiIndustryJob {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
|
|
// ESI fields
|
|
jobId Int @unique
|
|
installerId Int
|
|
installerName String? @db.VarChar(255)
|
|
facilityId BigInt
|
|
activityId Int // 1=manufacturing, 3=TE, 4=ME, 5=copying, 8=invention
|
|
blueprintId BigInt
|
|
blueprintTypeId Int
|
|
blueprintTypeName String? @db.VarChar(255)
|
|
blueprintLocationId BigInt
|
|
outputLocationId BigInt
|
|
runs Int
|
|
cost Decimal? @db.Decimal(19, 4) // Installation cost
|
|
licensedRuns Int?
|
|
probability Float?
|
|
productTypeId Int?
|
|
productTypeName String? @db.VarChar(255)
|
|
status String @db.VarChar(50) // active, cancelled, delivered, paused, ready, reverted
|
|
startDate DateTime
|
|
endDate DateTime
|
|
pauseDate DateTime?
|
|
completedDate DateTime?
|
|
completedCharacterId Int?
|
|
successfulRuns Int?
|
|
|
|
// ERP mapping - links to Project in ERP
|
|
projectId String? @db.VarChar(255)
|
|
isMapped Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([organizationId])
|
|
@@index([corporationId, status])
|
|
@@index([activityId])
|
|
@@map("esi_industry_jobs")
|
|
}
|
|
|
|
/// Cached ESI blueprints.
|
|
/// Maps to Fixed Assets with ME/TE as capital improvements.
|
|
model EsiBlueprint {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
|
|
// ESI fields
|
|
itemId BigInt @unique
|
|
typeId Int
|
|
typeName String? @db.VarChar(255)
|
|
locationId BigInt
|
|
locationFlag String @db.VarChar(100)
|
|
quantity Int // -1 = original, -2 = copy, >0 = stack of BPCs
|
|
materialEfficiency Int // 0-10
|
|
timeEfficiency Int // 0-20
|
|
runs Int // -1 = BPO (infinite), >0 = BPC runs remaining
|
|
|
|
// ERP mapping - links to FixedAsset
|
|
fixedAssetId String? @db.Uuid
|
|
isMapped Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([organizationId])
|
|
@@index([corporationId])
|
|
@@index([typeId])
|
|
@@map("esi_blueprints")
|
|
}
|
|
|
|
/// Cached ESI corporation contracts.
|
|
/// Maps to Purchase Orders with fulfillment tracking.
|
|
model EsiContract {
|
|
id String @id @default(uuid()) @db.Uuid
|
|
organizationId String @db.Uuid
|
|
corporationId Int
|
|
|
|
// ESI fields
|
|
contractId Int @unique
|
|
issuerId Int
|
|
issuerCorporationId Int
|
|
assigneeId Int
|
|
acceptorId Int @default(0)
|
|
contractType String @db.VarChar(50) // item_exchange, courier, auction, unknown
|
|
status String @db.VarChar(50) // outstanding, in_progress, finished, etc.
|
|
title String? @db.VarChar(255)
|
|
availability String @db.VarChar(50) // public, personal, corporation, alliance
|
|
forCorporation Boolean
|
|
dateIssued DateTime
|
|
dateExpired DateTime
|
|
dateAccepted DateTime?
|
|
dateCompleted DateTime?
|
|
daysToComplete Int?
|
|
price Decimal @db.Decimal(19, 4)
|
|
reward Decimal @default(0) @db.Decimal(19, 4)
|
|
collateral Decimal @default(0) @db.Decimal(19, 4)
|
|
buyout Decimal? @db.Decimal(19, 4)
|
|
volume Decimal? @db.Decimal(19, 4)
|
|
startLocationId BigInt?
|
|
endLocationId BigInt?
|
|
|
|
// ERP mapping
|
|
purchaseOrderId String? @db.VarChar(255)
|
|
isMapped Boolean @default(false)
|
|
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@index([organizationId])
|
|
@@index([corporationId, status])
|
|
@@index([contractType])
|
|
@@map("esi_contracts")
|
|
}
|