init: M1 scaffolding + M2 organization/clients/services CRUD
- monorepo (npm workspaces): apps/api (Fastify+Prisma+TS), apps/web (Vite+React+TS), packages/shared (zod schemas) - SSO via auth.queo.ru: jose+JWKS plugin, requireDocPermission(viewer|user|admin) - DEV_BYPASS_AUTH for local development (hard-checked off in production) - M2: organization upsert, clients CRUD with search, services catalog with soft-delete - BigInt -> Number serializer for Prisma money columns - Embedded Postgres + npm run dev:demo for one-command local boot - Docker compose for queoserver: postgres + api + web (nginx as ingress proxying /api -> api:3030) - First migration 0_init committed (prisma migrate diff) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,279 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DocType" AS ENUM ('contract', 'invoice', 'act', 'upd');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DocStatus" AS ENUM ('draft', 'issued', 'sent', 'partially_paid', 'paid', 'cancelled', 'signed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VatRate" AS ENUM ('none', '0', '5', '7', '10', '20');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ClientKind" AS ENUM ('ul', 'ip', 'fl');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TochkaEnv" AS ENUM ('sandbox', 'prod');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PaymentKind" AS ENUM ('incoming', 'incoming_sbp', 'incoming_sbp_b2b', 'outgoing');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Organization" (
|
||||
"id" UUID NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"inn" TEXT NOT NULL,
|
||||
"kpp" TEXT,
|
||||
"ogrn" TEXT,
|
||||
"legalAddress" TEXT,
|
||||
"bankName" TEXT,
|
||||
"bankBik" TEXT,
|
||||
"bankAccount" TEXT,
|
||||
"signatoryName" TEXT,
|
||||
"signatoryPosition" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Organization_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Client" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"kind" "ClientKind" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"inn" TEXT,
|
||||
"kpp" TEXT,
|
||||
"address" TEXT,
|
||||
"email" TEXT,
|
||||
"phone" TEXT,
|
||||
"contactPerson" TEXT,
|
||||
"requisitesJson" JSONB,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Client_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ServiceCatalog" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"unit" TEXT NOT NULL,
|
||||
"defaultPriceCents" BIGINT NOT NULL DEFAULT 0,
|
||||
"defaultVat" "VatRate" NOT NULL DEFAULT 'none',
|
||||
"notes" TEXT,
|
||||
"archivedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "ServiceCatalog_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DocumentTemplate" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"docType" "DocType" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"body" JSONB NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "DocumentTemplate_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Document" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"docType" "DocType" NOT NULL,
|
||||
"number" TEXT NOT NULL,
|
||||
"issuedAt" TIMESTAMP(3),
|
||||
"status" "DocStatus" NOT NULL DEFAULT 'draft',
|
||||
"clientId" UUID,
|
||||
"parentDocumentId" UUID,
|
||||
"body" JSONB NOT NULL,
|
||||
"totalCents" BIGINT NOT NULL DEFAULT 0,
|
||||
"vatCents" BIGINT NOT NULL DEFAULT 0,
|
||||
"currency" TEXT NOT NULL DEFAULT 'RUB',
|
||||
"tochkaDocumentId" TEXT,
|
||||
"tochkaEnvironment" "TochkaEnv",
|
||||
"pdfPath" TEXT,
|
||||
"createdBy" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Document_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DocumentLine" (
|
||||
"id" UUID NOT NULL,
|
||||
"documentId" UUID NOT NULL,
|
||||
"position" INTEGER NOT NULL,
|
||||
"serviceId" UUID,
|
||||
"name" TEXT NOT NULL,
|
||||
"qtyMilli" BIGINT NOT NULL DEFAULT 1000,
|
||||
"unit" TEXT NOT NULL,
|
||||
"priceCents" BIGINT NOT NULL,
|
||||
"vat" "VatRate" NOT NULL DEFAULT 'none',
|
||||
"sumCents" BIGINT NOT NULL,
|
||||
|
||||
CONSTRAINT "DocumentLine_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Payment" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"documentId" UUID,
|
||||
"tochkaPaymentId" TEXT NOT NULL,
|
||||
"kind" "PaymentKind" NOT NULL,
|
||||
"amountCents" BIGINT NOT NULL,
|
||||
"payerInn" TEXT,
|
||||
"payerName" TEXT,
|
||||
"purpose" TEXT,
|
||||
"paidAt" TIMESTAMP(3),
|
||||
"raw" JSONB NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "Payment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TochkaCredential" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"environment" "TochkaEnv" NOT NULL,
|
||||
"jwtEncrypted" TEXT NOT NULL,
|
||||
"customerCode" TEXT NOT NULL,
|
||||
"accountCode" TEXT,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "TochkaCredential_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WebhookEvent" (
|
||||
"id" UUID NOT NULL,
|
||||
"receivedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"source" TEXT NOT NULL,
|
||||
"eventType" TEXT NOT NULL,
|
||||
"raw" JSONB NOT NULL,
|
||||
"processedAt" TIMESTAMP(3),
|
||||
"error" TEXT,
|
||||
"dedupeKey" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "WebhookEvent_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AuditLog" (
|
||||
"id" UUID NOT NULL,
|
||||
"organizationId" UUID NOT NULL,
|
||||
"actorSub" TEXT,
|
||||
"action" TEXT NOT NULL,
|
||||
"entity" TEXT NOT NULL,
|
||||
"entityId" TEXT NOT NULL,
|
||||
"diff" JSONB,
|
||||
"at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Client_organizationId_idx" ON "Client"("organizationId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Client_organizationId_name_idx" ON "Client"("organizationId", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ServiceCatalog_organizationId_idx" ON "ServiceCatalog"("organizationId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ServiceCatalog_organizationId_archivedAt_idx" ON "ServiceCatalog"("organizationId", "archivedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DocumentTemplate_organizationId_docType_idx" ON "DocumentTemplate"("organizationId", "docType");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Document_organizationId_clientId_issuedAt_idx" ON "Document"("organizationId", "clientId", "issuedAt" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Document_organizationId_status_idx" ON "Document"("organizationId", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Document_tochkaDocumentId_idx" ON "Document"("tochkaDocumentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Document_organizationId_docType_number_key" ON "Document"("organizationId", "docType", "number");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DocumentLine_documentId_idx" ON "DocumentLine"("documentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DocumentLine_serviceId_idx" ON "DocumentLine"("serviceId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Payment_tochkaPaymentId_key" ON "Payment"("tochkaPaymentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_organizationId_paidAt_idx" ON "Payment"("organizationId", "paidAt" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Payment_documentId_idx" ON "Payment"("documentId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TochkaCredential_organizationId_environment_key" ON "TochkaCredential"("organizationId", "environment");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WebhookEvent_dedupeKey_key" ON "WebhookEvent"("dedupeKey");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "WebhookEvent_source_eventType_receivedAt_idx" ON "WebhookEvent"("source", "eventType", "receivedAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AuditLog_organizationId_at_idx" ON "AuditLog"("organizationId", "at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "AuditLog_entity_entityId_idx" ON "AuditLog"("entity", "entityId");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Client" ADD CONSTRAINT "Client_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ServiceCatalog" ADD CONSTRAINT "ServiceCatalog_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "DocumentTemplate" ADD CONSTRAINT "DocumentTemplate_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Document" ADD CONSTRAINT "Document_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Document" ADD CONSTRAINT "Document_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Document" ADD CONSTRAINT "Document_parentDocumentId_fkey" FOREIGN KEY ("parentDocumentId") REFERENCES "Document"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "DocumentLine" ADD CONSTRAINT "DocumentLine_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "DocumentLine" ADD CONSTRAINT "DocumentLine_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "ServiceCatalog"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TochkaCredential" ADD CONSTRAINT "TochkaCredential_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "Organization"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
@@ -0,0 +1,250 @@
|
||||
// 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
|
||||
inn String
|
||||
kpp String?
|
||||
ogrn String?
|
||||
legalAddress String?
|
||||
bankName String?
|
||||
bankBik String?
|
||||
bankAccount String?
|
||||
signatoryName String?
|
||||
signatoryPosition String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
clients Client[]
|
||||
servicesCatalog ServiceCatalog[]
|
||||
templates DocumentTemplate[]
|
||||
documents Document[]
|
||||
payments Payment[]
|
||||
tochkaCredentials TochkaCredential[]
|
||||
auditLog AuditLog[]
|
||||
}
|
||||
|
||||
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])
|
||||
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])
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
async function main() {
|
||||
const id = process.env.DEFAULT_ORGANIZATION_ID ?? '00000000-0000-0000-0000-000000000001';
|
||||
|
||||
const existing = await prisma.organization.findUnique({ where: { id } });
|
||||
if (existing) {
|
||||
console.log(`organization ${id} already exists — skipping seed`);
|
||||
return;
|
||||
}
|
||||
|
||||
await prisma.organization.create({
|
||||
data: {
|
||||
id,
|
||||
name: 'ООО «Моя компания»',
|
||||
inn: '0000000000',
|
||||
// Остальные реквизиты пользователь заполнит через UI на странице «Реквизиты».
|
||||
},
|
||||
});
|
||||
console.log(`organization ${id} created (заполните реквизиты в UI: /organization)`);
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect();
|
||||
});
|
||||
Reference in New Issue
Block a user