278 lines
9.2 KiB
Plaintext
278 lines
9.2 KiB
Plaintext
// AOS — Agency Operating System
|
|
// One deployment = one agency (single tenant). SQLite for this build.
|
|
// Money is integer cents, tax rates are basis points. No floats for money.
|
|
|
|
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "sqlite"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Platform
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
email String @unique
|
|
name String
|
|
role String @default("owner") // owner|admin|sales|pm|developer|finance|readonly|client
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// Module registry — installed/enabled/disabled state of each domain module
|
|
model Module {
|
|
id String @id // crm|leads|projects|licenses|sales-docs|finance|support|...
|
|
version String @default("1.0.0")
|
|
enabled Boolean @default(true)
|
|
installed Boolean @default(true)
|
|
nav String? // JSON: [{label,route,permission}]
|
|
events String? // JSON: {emits:[],listens:[]}
|
|
updatedAt DateTime @updatedAt
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
model Setting {
|
|
key String @id
|
|
value String // typed at app layer (encrypted secrets handled separately)
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model AuditLog {
|
|
id String @id @default(cuid())
|
|
actor String? // user id or "system"
|
|
action String // e.g. "crm.deal:update"
|
|
entity String
|
|
entityId String
|
|
before String? // JSON
|
|
after String? // JSON
|
|
hash String? // hash chain
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CRM (Phase 1)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model Company {
|
|
id String @id @default(cuid())
|
|
name String
|
|
legalForm String? // GmbH, UG, e.K., GbR, AG, Einzelunternehmen...
|
|
vatId String? // USt-IdNr
|
|
registerCourt String?
|
|
registerNumber String? // HRB ...
|
|
website String?
|
|
email String?
|
|
phone String?
|
|
city String?
|
|
zip String?
|
|
street String?
|
|
source String? // lead-finder|import|manual|referral
|
|
tags String? // comma separated
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
persons Person[]
|
|
deals Deal[]
|
|
leads Lead[]
|
|
licenses License[]
|
|
invoices Invoice[]
|
|
projects Project[]
|
|
}
|
|
|
|
model Person {
|
|
id String @id @default(cuid())
|
|
companyId String?
|
|
company Company? @relation(fields: [companyId], references: [id])
|
|
firstName String
|
|
lastName String
|
|
email String?
|
|
phone String?
|
|
role String? // Geschäftsführer, Marketing, IT...
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model Deal {
|
|
id String @id @default(cuid())
|
|
companyId String
|
|
company Company @relation(fields: [companyId], references: [id])
|
|
title String
|
|
valueCents Int @default(0)
|
|
probability Int @default(0) // 0-100
|
|
stage String @default("lead") // pipeline stage key
|
|
expectedClose DateTime?
|
|
lossReason String?
|
|
ownerId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Leads Finder (Phase 2)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model Lead {
|
|
id String @id @default(cuid())
|
|
// discovered company info (may become a Company when approved)
|
|
companyName String
|
|
website String?
|
|
city String?
|
|
branch String?
|
|
// audit + scoring
|
|
score Int @default(0) // 0-100 weighted rule score
|
|
scoreLabel String? // new-site|redesign|seo|aeo|shop|maintenance|accessibility
|
|
evidence String? // JSON array of {id,check,status,detail}
|
|
explanation String? // LLM draft "why this lead" — every claim links to an evidence id
|
|
suggestedAngle String? // proposed outreach angle
|
|
status String @default("pending") // pending|approved|rejected|snoozed|converted
|
|
reviewReason String?
|
|
source String? // serpapi|tavily|apify|firecrawl
|
|
radiusKm Int?
|
|
// when converted, link to CRM company + deal
|
|
companyId String?
|
|
company Company? @relation(fields: [companyId], references: [id])
|
|
createdAt DateTime @default(now())
|
|
reviewedAt DateTime?
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Projects (Phase 3)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model Project {
|
|
id String @id @default(cuid())
|
|
companyId String
|
|
company Company @relation(fields: [companyId], references: [id])
|
|
name String
|
|
type String // website|shop|app|retainer
|
|
status String @default("planning") // planning|active|review|done|on-hold|cancelled
|
|
budgetType String @default("fixed") // fixed|time-materials|retainer
|
|
budgetHours Int?
|
|
spentHours Int @default(0)
|
|
fixedPriceCents Int?
|
|
startDate DateTime?
|
|
dueDate DateTime?
|
|
progress Int @default(0) // 0-100
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tasks Task[]
|
|
timeEntries TimeEntry[]
|
|
}
|
|
|
|
model Task {
|
|
id String @id @default(cuid())
|
|
projectId String
|
|
project Project @relation(fields: [projectId], references: [id])
|
|
title String
|
|
description String?
|
|
status String @default("todo") // todo|in-progress|review|done
|
|
assignee String?
|
|
estimateMin Int?
|
|
priority String @default("medium") // low|medium|high
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
model TimeEntry {
|
|
id String @id @default(cuid())
|
|
projectId String
|
|
project Project @relation(fields: [projectId], references: [id])
|
|
userId String?
|
|
description String
|
|
minutes Int
|
|
billable Boolean @default(true)
|
|
rateCentsPerHour Int?
|
|
invoiced Boolean @default(false)
|
|
date DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Licenses (Phase 4)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model License {
|
|
id String @id @default(cuid())
|
|
key String @unique
|
|
companyId String
|
|
company Company @relation(fields: [companyId], references: [id])
|
|
product String // e.g. aos-cms, wp-plugin-xyz
|
|
plan String // starter|pro|enterprise
|
|
status String @default("active") // active|suspended|revoked|expired
|
|
maxActivations Int @default(1)
|
|
validUntil DateTime?
|
|
token String? // Ed25519-signed token (mocked)
|
|
createdAt DateTime @default(now())
|
|
activations LicenseActivation[]
|
|
}
|
|
|
|
model LicenseActivation {
|
|
id String @id @default(cuid())
|
|
licenseId String
|
|
license License @relation(fields: [licenseId], references: [id])
|
|
domain String
|
|
fingerprint String?
|
|
environment String @default("production") // production|staging
|
|
version String?
|
|
lastCheck DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
}
|
|
|
|
// Asset & renewal register (what the agency manages/pays for on behalf of clients)
|
|
model Asset {
|
|
id String @id @default(cuid())
|
|
name String
|
|
type String // domain|hosting|ssl|plugin-seat|saas
|
|
provider String? // Ionos, Strato, Cloudflare...
|
|
companyId String? // may be internal asset
|
|
costCentsPerMonth Int?
|
|
renewalDate DateTime
|
|
rebill Boolean @default(false)
|
|
markupPct Int @default(0)
|
|
status String @default("active") // active|expiring|expired|cancelled
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Finance (Phase 5)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
model Invoice {
|
|
id String @id @default(cuid())
|
|
number String @unique // gapless, e.g. RE-2026-0001
|
|
companyId String
|
|
company Company @relation(fields: [companyId], references: [id])
|
|
status String @default("draft") // draft|sent|paid|overdue|cancelled|storno
|
|
type String @default("final") // final|partial|recurring|credit
|
|
issueDate DateTime @default(now())
|
|
dueDate DateTime
|
|
paidDate DateTime?
|
|
netCents Int @default(0)
|
|
taxBp Int @default(1900) // 19% as basis points
|
|
grossCents Int @default(0)
|
|
// E-Rechnung / compliance
|
|
zugferd Boolean @default(false)
|
|
xrechnung Boolean @default(false)
|
|
datevExported Boolean @default(false)
|
|
projectId String?
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
lines InvoiceLine[]
|
|
}
|
|
|
|
model InvoiceLine {
|
|
id String @id @default(cuid())
|
|
invoiceId String
|
|
invoice Invoice @relation(fields: [invoiceId], references: [id])
|
|
description String
|
|
quantity Int @default(1)
|
|
unitCents Int
|
|
totalCents Int
|
|
}
|