// Doc_manager — модель данных // Полиморфная таблица documents для contract/invoice/act/upd. // organization_id присутствует на всех owner-scoped таблицах (single-tenant v1, готово к multi-tenant). generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } enum DocType { contract invoice act upd } enum DocStatus { draft issued sent partially_paid paid cancelled signed } enum VatRate { none vat_0 @map("0") vat_5 @map("5") vat_7 @map("7") vat_10 @map("10") vat_20 @map("20") } enum ClientKind { ul // юр.лицо ip // ИП fl // физ.лицо } enum TochkaEnv { sandbox prod } enum PaymentKind { incoming incoming_sbp incoming_sbp_b2b outgoing } model Organization { id String @id @default(uuid()) @db.Uuid name String shortName String? // короткое имя для бейджей в UI / селектора inn String kpp String? ogrn String? legalAddress String? // Поля bankName/bankBik/bankAccount устарели после введения BankAccount. // Оставлены для обратной совместимости с уже сохранёнными данными. bankName String? bankBik String? bankAccount String? signatoryName String? signatoryPosition String? archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt clients Client[] servicesCatalog ServiceCatalog[] templates DocumentTemplate[] documents Document[] payments Payment[] tochkaCredentials TochkaCredential[] auditLog AuditLog[] bankAccounts BankAccount[] } model BankAccount { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade) name String // отображаемое имя, напр. "Точка — основной" bankName String? bankBik String? accountNumber String? // р/счёт (20 цифр) corrAccount String? // к/счёт (20 цифр) currency String @default("RUB") isPrimary Boolean @default(false) archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt tochkaCredentials TochkaCredential[] @@index([organizationId]) } model Client { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) kind ClientKind name String inn String? kpp String? address String? email String? phone String? contactPerson String? requisitesJson Json? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt documents Document[] @@index([organizationId]) @@index([organizationId, name]) } model ServiceCatalog { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) name String unit String // "шт", "час", "мес" defaultPriceCents BigInt @default(0) defaultVat VatRate @default(none) notes String? archivedAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lines DocumentLine[] @@index([organizationId]) @@index([organizationId, archivedAt]) } model DocumentTemplate { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) docType DocType name String body Json createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([organizationId, docType]) } model Document { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) docType DocType number String issuedAt DateTime? status DocStatus @default(draft) clientId String? @db.Uuid client Client? @relation(fields: [clientId], references: [id]) parentDocumentId String? @db.Uuid parent Document? @relation("DocumentChildren", fields: [parentDocumentId], references: [id]) children Document[] @relation("DocumentChildren") body Json totalCents BigInt @default(0) vatCents BigInt @default(0) currency String @default("RUB") tochkaDocumentId String? tochkaEnvironment TochkaEnv? pdfPath String? createdBy String? // sub из JWT auth.queo.ru createdAt DateTime @default(now()) updatedAt DateTime @updatedAt lines DocumentLine[] payments Payment[] @@unique([organizationId, docType, number]) @@index([organizationId, clientId, issuedAt(sort: Desc)]) @@index([organizationId, status]) @@index([tochkaDocumentId]) } model DocumentLine { id String @id @default(uuid()) @db.Uuid documentId String @db.Uuid document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) position Int serviceId String? @db.Uuid service ServiceCatalog? @relation(fields: [serviceId], references: [id]) name String // Количество хранится как milli-units (тысячные), чтобы поддержать дробные количества без float. qtyMilli BigInt @default(1000) unit String priceCents BigInt vat VatRate @default(none) sumCents BigInt @@index([documentId]) @@index([serviceId]) } model Payment { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) documentId String? @db.Uuid document Document? @relation(fields: [documentId], references: [id]) tochkaPaymentId String @unique kind PaymentKind amountCents BigInt payerInn String? payerName String? purpose String? paidAt DateTime? raw Json createdAt DateTime @default(now()) @@index([organizationId, paidAt(sort: Desc)]) @@index([documentId]) } model TochkaCredential { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) bankAccountId String? @db.Uuid bankAccount BankAccount? @relation(fields: [bankAccountId], references: [id]) environment TochkaEnv // AES-256-GCM ciphertext (iv|tag|ct), base64 jwtEncrypted String customerCode String accountCode String? expiresAt DateTime? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@unique([organizationId, environment]) } model WebhookEvent { id String @id @default(uuid()) @db.Uuid receivedAt DateTime @default(now()) source String // 'tochka' eventType String raw Json processedAt DateTime? error String? dedupeKey String @unique @@index([source, eventType, receivedAt]) } model AuditLog { id String @id @default(uuid()) @db.Uuid organizationId String @db.Uuid organization Organization @relation(fields: [organizationId], references: [id]) actorSub String? action String entity String entityId String diff Json? at DateTime @default(now()) @@index([organizationId, at(sort: Desc)]) @@index([entity, entityId]) }