Monorepo skeleton, Prisma schema, core domain and services with tests

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zabudko-ruandClaude Opus 5.5 committed 2026-10-17 19:45:51 +04:00
commit 45d7512d2b
64 files changed
+9483

No files matched your search

@@ -0,0 +1,824 @@
-- CreateEnum
CREATE TYPE "Locale" AS ENUM ('ru', 'en', 'zh', 'hy', 'es');
-- CreateEnum
CREATE TYPE "CategoryCode" AS ENUM ('HOME', 'DOCS', 'LEARNING', 'PERKS');
-- CreateEnum
CREATE TYPE "ServiceFlow" AS ENUM ('CLEANING', 'REPAIR', 'TRANSFER', 'LANGUAGE', 'CAREER', 'DOCUMENTS');
-- CreateEnum
CREATE TYPE "ProviderRole" AS ENUM ('CLEANER', 'HANDYMAN', 'DRIVER', 'TEACHER', 'CAREER_CONSULTANT', 'DOCS_AGENT');
-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('NEW', 'ASSIGNED', 'EN_ROUTE', 'CLEANING', 'ON_SITE', 'ESTIMATE_APPROVED', 'REPAIRING', 'DRIVER_WAITING', 'RIDE_IN_PROGRESS', 'ENROLLED', 'LESSON_SCHEDULED', 'LESSON_IN_PROGRESS', 'LESSON_DONE', 'CONSULTANT_ASSIGNED', 'RESUME_RECEIVED', 'MEETING', 'PLAN_SENT', 'DOCS_REVIEW', 'NEEDS_MORE_DOCS', 'SUBMITTED', 'DONE', 'COMPLETED', 'CANCELLED');
-- CreateEnum
CREATE TYPE "ActorType" AS ENUM ('CLIENT', 'PROVIDER', 'DISPATCHER', 'ADMIN', 'SYSTEM');
-- CreateEnum
CREATE TYPE "OfferStatus" AS ENUM ('OFFERED', 'ACCEPTED', 'DECLINED', 'EXPIRED', 'REVOKED');
-- CreateEnum
CREATE TYPE "PaymentStatus" AS ENUM ('UNPAID', 'PAID', 'REFUNDED');
-- CreateEnum
CREATE TYPE "RateType" AS ENUM ('FIXED', 'PERCENT');
-- CreateEnum
CREATE TYPE "AdminRole" AS ENUM ('OWNER', 'OPERATOR');
-- CreateEnum
CREATE TYPE "SubscriptionPlan" AS ENUM ('BAREV_PLUS');
-- CreateEnum
CREATE TYPE "SubscriptionSource" AS ENUM ('AGENCY_GIFT', 'MANUAL', 'PAID');
-- CreateEnum
CREATE TYPE "SubscriptionStatus" AS ENUM ('ACTIVE', 'EXPIRED', 'CANCELLED');
-- CreateEnum
CREATE TYPE "PromoKind" AS ENUM ('AGENCY_GIFT', 'DISCOUNT');
-- CreateEnum
CREATE TYPE "FileKind" AS ENUM ('PHOTO_REPORT', 'CLIENT_PHOTO', 'DOCUMENT_SCAN', 'RESUME', 'OTHER');
-- CreateEnum
CREATE TYPE "EstimateStatus" AS ENUM ('PENDING', 'ACCEPTED', 'REJECTED');
-- CreateEnum
CREATE TYPE "TaskStatus" AS ENUM ('OPEN', 'DONE');
-- CreateEnum
CREATE TYPE "PayoutStatus" AS ENUM ('DRAFT', 'APPROVED', 'PAID');
-- CreateTable
CREATE TABLE "User" (
"id" TEXT NOT NULL,
"telegramId" BIGINT NOT NULL,
"username" TEXT,
"firstName" TEXT,
"lastName" TEXT,
"phone" TEXT,
"locale" "Locale" NOT NULL DEFAULT 'ru',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Client" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"consentAt" TIMESTAMP(3),
"consentVersion" TEXT,
"agencyId" TEXT,
"agencyAttachedAt" TIMESTAMP(3),
"source" TEXT NOT NULL DEFAULT 'organic',
"utm" JSONB,
"giftUsedAt" TIMESTAMP(3),
"cardCode" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Client_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProviderCompany" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"roles" "ProviderRole"[],
"rateType" "RateType" NOT NULL DEFAULT 'PERCENT',
"rateValue" INTEGER NOT NULL DEFAULT 70,
"contactPhone" TEXT,
"inviteCode" TEXT NOT NULL,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "ProviderCompany_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CompanyDispatcher" (
"id" TEXT NOT NULL,
"companyId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CompanyDispatcher_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Provider" (
"id" TEXT NOT NULL,
"userId" TEXT,
"name" TEXT NOT NULL,
"phone" TEXT,
"role" "ProviderRole" NOT NULL,
"companyId" TEXT,
"languages" "Locale"[],
"rateType" "RateType" NOT NULL DEFAULT 'PERCENT',
"rateValue" INTEGER NOT NULL DEFAULT 70,
"ratingSum" INTEGER NOT NULL DEFAULT 0,
"ratingCount" INTEGER NOT NULL DEFAULT 0,
"onShift" BOOLEAN NOT NULL DEFAULT false,
"active" BOOLEAN NOT NULL DEFAULT true,
"inviteCode" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Provider_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "District" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "District_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProviderDistrict" (
"providerId" TEXT NOT NULL,
"districtId" TEXT NOT NULL,
CONSTRAINT "ProviderDistrict_pkey" PRIMARY KEY ("providerId","districtId")
);
-- CreateTable
CREATE TABLE "ProviderSchedule" (
"id" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"weekday" INTEGER NOT NULL,
"startMinute" INTEGER NOT NULL,
"endMinute" INTEGER NOT NULL,
CONSTRAINT "ProviderSchedule_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Category" (
"id" TEXT NOT NULL,
"code" "CategoryCode" NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"barevPlusDiscountPct" INTEGER NOT NULL DEFAULT 0,
"active" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Service" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"categoryId" TEXT NOT NULL,
"flow" "ServiceFlow" NOT NULL,
"providerRole" "ProviderRole" NOT NULL,
"pricing" JSONB NOT NULL,
"paramsSchema" JSONB NOT NULL,
"slotConfig" JSONB NOT NULL,
"lateCancelFeePct" INTEGER NOT NULL DEFAULT 30,
"isOnline" BOOLEAN NOT NULL DEFAULT false,
"active" BOOLEAN NOT NULL DEFAULT true,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Service_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ChecklistItem" (
"id" TEXT NOT NULL,
"serviceId" TEXT NOT NULL,
"code" TEXT NOT NULL,
"requiresPhoto" BOOLEAN NOT NULL DEFAULT false,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "ChecklistItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PaymentMethod" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"name" JSONB NOT NULL,
"active" BOOLEAN NOT NULL DEFAULT true,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "PaymentMethod_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Order" (
"id" TEXT NOT NULL,
"number" SERIAL NOT NULL,
"clientId" TEXT NOT NULL,
"serviceId" TEXT NOT NULL,
"params" JSONB NOT NULL,
"scheduledAt" TIMESTAMP(3) NOT NULL,
"durationMinutes" INTEGER NOT NULL,
"isOnline" BOOLEAN NOT NULL DEFAULT false,
"addressText" TEXT,
"districtId" TEXT,
"lat" DOUBLE PRECISION,
"lng" DOUBLE PRECISION,
"note" TEXT,
"locale" "Locale" NOT NULL,
"basePrice" INTEGER NOT NULL,
"discountPct" INTEGER NOT NULL DEFAULT 0,
"discountAmount" INTEGER NOT NULL DEFAULT 0,
"totalPrice" INTEGER NOT NULL,
"status" "OrderStatus" NOT NULL DEFAULT 'NEW',
"providerId" TEXT,
"companyId" TEXT,
"paymentMethodId" TEXT,
"paymentStatus" "PaymentStatus" NOT NULL DEFAULT 'UNPAID',
"paidAt" TIMESTAMP(3),
"cancelledAt" TIMESTAMP(3),
"cancelFee" INTEGER NOT NULL DEFAULT 0,
"cancelReason" TEXT,
"completedAt" TIMESTAMP(3),
"repeatOfId" TEXT,
"reminded24hAt" TIMESTAMP(3),
"reminded1hAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderStatusHistory" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"fromStatus" "OrderStatus",
"toStatus" "OrderStatus" NOT NULL,
"event" TEXT,
"actorType" "ActorType" NOT NULL,
"actorId" TEXT,
"comment" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "OrderStatusHistory_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderOffer" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"providerId" TEXT,
"companyId" TEXT,
"status" "OfferStatus" NOT NULL DEFAULT 'OFFERED',
"expiresAt" TIMESTAMP(3),
"respondedAt" TIMESTAMP(3),
"createdById" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "OrderOffer_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderChecklistItem" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"code" TEXT NOT NULL,
"requiresPhoto" BOOLEAN NOT NULL DEFAULT false,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
"checkedAt" TIMESTAMP(3),
"fileId" TEXT,
CONSTRAINT "OrderChecklistItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderFile" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"kind" "FileKind" NOT NULL,
"storageKey" TEXT NOT NULL,
"mime" TEXT NOT NULL,
"size" INTEGER NOT NULL,
"uploadedBy" "ActorType" NOT NULL,
"uploadedById" TEXT,
"deleteAfter" TIMESTAMP(3),
"deletedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "OrderFile_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "FileAccessLog" (
"id" TEXT NOT NULL,
"fileId" TEXT NOT NULL,
"actorType" "ActorType" NOT NULL,
"actorId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "FileAccessLog_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Estimate" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"items" JSONB NOT NULL,
"total" INTEGER NOT NULL,
"status" "EstimateStatus" NOT NULL DEFAULT 'PENDING',
"respondedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Estimate_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Message" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"fromType" "ActorType" NOT NULL,
"fromUserId" TEXT,
"text" TEXT,
"fileId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Message_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Rating" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"clientId" TEXT NOT NULL,
"providerId" TEXT,
"score" INTEGER NOT NULL,
"comment" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Rating_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LessonPackage" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"total" INTEGER NOT NULL,
"completed" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "LessonPackage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Payment" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"provider" TEXT NOT NULL,
"amount" INTEGER NOT NULL,
"status" "PaymentStatus" NOT NULL,
"externalId" TEXT,
"markedById" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Payment_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OperatorTask" (
"id" TEXT NOT NULL,
"type" TEXT NOT NULL,
"orderId" TEXT,
"status" "TaskStatus" NOT NULL DEFAULT 'OPEN',
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"doneAt" TIMESTAMP(3),
CONSTRAINT "OperatorTask_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Agency" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"name" TEXT NOT NULL,
"commissionPct" INTEGER NOT NULL DEFAULT 10,
"contact" TEXT,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Agency_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AgencyCommission" (
"id" TEXT NOT NULL,
"agencyId" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"pct" INTEGER NOT NULL,
"amount" INTEGER NOT NULL,
"period" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AgencyCommission_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PromoCode" (
"id" TEXT NOT NULL,
"code" TEXT NOT NULL,
"kind" "PromoKind" NOT NULL,
"agencyId" TEXT,
"discountPct" INTEGER,
"maxUses" INTEGER,
"usedCount" INTEGER NOT NULL DEFAULT 0,
"validFrom" TIMESTAMP(3),
"validUntil" TIMESTAMP(3),
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PromoCode_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PromoCodeUse" (
"id" TEXT NOT NULL,
"promoId" TEXT NOT NULL,
"clientId" TEXT NOT NULL,
"orderId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PromoCodeUse_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Subscription" (
"id" TEXT NOT NULL,
"clientId" TEXT NOT NULL,
"plan" "SubscriptionPlan" NOT NULL DEFAULT 'BAREV_PLUS',
"source" "SubscriptionSource" NOT NULL,
"status" "SubscriptionStatus" NOT NULL DEFAULT 'ACTIVE',
"startsAt" TIMESTAMP(3) NOT NULL,
"endsAt" TIMESTAMP(3) NOT NULL,
"remindedAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Subscription_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Partner" (
"id" TEXT NOT NULL,
"category" TEXT NOT NULL,
"name" TEXT NOT NULL,
"offer" JSONB NOT NULL,
"terms" JSONB,
"address" TEXT,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Partner_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CardUsage" (
"id" TEXT NOT NULL,
"clientId" TEXT NOT NULL,
"partnerId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CardUsage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Payout" (
"id" TEXT NOT NULL,
"providerId" TEXT,
"companyId" TEXT,
"periodStart" TIMESTAMP(3) NOT NULL,
"periodEnd" TIMESTAMP(3) NOT NULL,
"amount" INTEGER NOT NULL,
"status" "PayoutStatus" NOT NULL DEFAULT 'DRAFT',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Payout_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AdminUser" (
"id" TEXT NOT NULL,
"email" TEXT NOT NULL,
"name" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"role" "AdminRole" NOT NULL DEFAULT 'OPERATOR',
"totpSecret" TEXT,
"totpEnabled" BOOLEAN NOT NULL DEFAULT false,
"active" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AdminUser_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AdminSession" (
"id" TEXT NOT NULL,
"adminUserId" TEXT NOT NULL,
"tokenHash" TEXT NOT NULL,
"verified" BOOLEAN NOT NULL DEFAULT false,
"expiresAt" TIMESTAMP(3) NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AdminSession_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AuditLog" (
"id" TEXT NOT NULL,
"adminUserId" TEXT,
"action" TEXT NOT NULL,
"entity" TEXT NOT NULL,
"entityId" TEXT,
"data" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Setting" (
"key" TEXT NOT NULL,
"value" JSONB NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Setting_pkey" PRIMARY KEY ("key")
);
-- CreateTable
CREATE TABLE "StartToken" (
"id" TEXT NOT NULL,
"locale" "Locale",
"agency" TEXT,
"utm" JSONB,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "StartToken_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "User_telegramId_key" ON "User"("telegramId");
-- CreateIndex
CREATE UNIQUE INDEX "Client_userId_key" ON "Client"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "Client_cardCode_key" ON "Client"("cardCode");
-- CreateIndex
CREATE INDEX "Client_agencyId_idx" ON "Client"("agencyId");
-- CreateIndex
CREATE UNIQUE INDEX "ProviderCompany_inviteCode_key" ON "ProviderCompany"("inviteCode");
-- CreateIndex
CREATE UNIQUE INDEX "CompanyDispatcher_companyId_userId_key" ON "CompanyDispatcher"("companyId", "userId");
-- CreateIndex
CREATE UNIQUE INDEX "Provider_userId_key" ON "Provider"("userId");
-- CreateIndex
CREATE UNIQUE INDEX "Provider_inviteCode_key" ON "Provider"("inviteCode");
-- CreateIndex
CREATE INDEX "Provider_role_active_idx" ON "Provider"("role", "active");
-- CreateIndex
CREATE UNIQUE INDEX "District_code_key" ON "District"("code");
-- CreateIndex
CREATE INDEX "ProviderSchedule_providerId_weekday_idx" ON "ProviderSchedule"("providerId", "weekday");
-- CreateIndex
CREATE UNIQUE INDEX "Category_code_key" ON "Category"("code");
-- CreateIndex
CREATE UNIQUE INDEX "Service_code_key" ON "Service"("code");
-- CreateIndex
CREATE UNIQUE INDEX "ChecklistItem_serviceId_code_key" ON "ChecklistItem"("serviceId", "code");
-- CreateIndex
CREATE UNIQUE INDEX "PaymentMethod_code_key" ON "PaymentMethod"("code");
-- CreateIndex
CREATE UNIQUE INDEX "Order_number_key" ON "Order"("number");
-- CreateIndex
CREATE INDEX "Order_status_idx" ON "Order"("status");
-- CreateIndex
CREATE INDEX "Order_scheduledAt_idx" ON "Order"("scheduledAt");
-- CreateIndex
CREATE INDEX "Order_clientId_idx" ON "Order"("clientId");
-- CreateIndex
CREATE INDEX "Order_providerId_scheduledAt_idx" ON "Order"("providerId", "scheduledAt");
-- CreateIndex
CREATE INDEX "OrderStatusHistory_orderId_createdAt_idx" ON "OrderStatusHistory"("orderId", "createdAt");
-- CreateIndex
CREATE INDEX "OrderOffer_orderId_status_idx" ON "OrderOffer"("orderId", "status");
-- CreateIndex
CREATE INDEX "OrderOffer_providerId_status_idx" ON "OrderOffer"("providerId", "status");
-- CreateIndex
CREATE INDEX "OrderOffer_companyId_status_idx" ON "OrderOffer"("companyId", "status");
-- CreateIndex
CREATE UNIQUE INDEX "OrderChecklistItem_fileId_key" ON "OrderChecklistItem"("fileId");
-- CreateIndex
CREATE UNIQUE INDEX "OrderChecklistItem_orderId_code_key" ON "OrderChecklistItem"("orderId", "code");
-- CreateIndex
CREATE UNIQUE INDEX "OrderFile_storageKey_key" ON "OrderFile"("storageKey");
-- CreateIndex
CREATE INDEX "Message_orderId_createdAt_idx" ON "Message"("orderId", "createdAt");
-- CreateIndex
CREATE UNIQUE INDEX "Rating_orderId_key" ON "Rating"("orderId");
-- CreateIndex
CREATE UNIQUE INDEX "LessonPackage_orderId_key" ON "LessonPackage"("orderId");
-- CreateIndex
CREATE UNIQUE INDEX "Agency_code_key" ON "Agency"("code");
-- CreateIndex
CREATE UNIQUE INDEX "AgencyCommission_orderId_key" ON "AgencyCommission"("orderId");
-- CreateIndex
CREATE INDEX "AgencyCommission_agencyId_period_idx" ON "AgencyCommission"("agencyId", "period");
-- CreateIndex
CREATE UNIQUE INDEX "PromoCode_code_key" ON "PromoCode"("code");
-- CreateIndex
CREATE UNIQUE INDEX "PromoCodeUse_promoId_clientId_key" ON "PromoCodeUse"("promoId", "clientId");
-- CreateIndex
CREATE INDEX "Subscription_clientId_endsAt_idx" ON "Subscription"("clientId", "endsAt");
-- CreateIndex
CREATE UNIQUE INDEX "AdminUser_email_key" ON "AdminUser"("email");
-- CreateIndex
CREATE UNIQUE INDEX "AdminSession_tokenHash_key" ON "AdminSession"("tokenHash");
-- CreateIndex
CREATE INDEX "AuditLog_entity_entityId_idx" ON "AuditLog"("entity", "entityId");
-- AddForeignKey
ALTER TABLE "Client" ADD CONSTRAINT "Client_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Client" ADD CONSTRAINT "Client_agencyId_fkey" FOREIGN KEY ("agencyId") REFERENCES "Agency"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CompanyDispatcher" ADD CONSTRAINT "CompanyDispatcher_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "ProviderCompany"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CompanyDispatcher" ADD CONSTRAINT "CompanyDispatcher_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Provider" ADD CONSTRAINT "Provider_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Provider" ADD CONSTRAINT "Provider_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "ProviderCompany"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProviderDistrict" ADD CONSTRAINT "ProviderDistrict_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProviderDistrict" ADD CONSTRAINT "ProviderDistrict_districtId_fkey" FOREIGN KEY ("districtId") REFERENCES "District"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProviderSchedule" ADD CONSTRAINT "ProviderSchedule_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Service" ADD CONSTRAINT "Service_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ChecklistItem" ADD CONSTRAINT "ChecklistItem_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_serviceId_fkey" FOREIGN KEY ("serviceId") REFERENCES "Service"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_districtId_fkey" FOREIGN KEY ("districtId") REFERENCES "District"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "ProviderCompany"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_paymentMethodId_fkey" FOREIGN KEY ("paymentMethodId") REFERENCES "PaymentMethod"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderStatusHistory" ADD CONSTRAINT "OrderStatusHistory_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderOffer" ADD CONSTRAINT "OrderOffer_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderOffer" ADD CONSTRAINT "OrderOffer_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderOffer" ADD CONSTRAINT "OrderOffer_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "ProviderCompany"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderChecklistItem" ADD CONSTRAINT "OrderChecklistItem_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderChecklistItem" ADD CONSTRAINT "OrderChecklistItem_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "OrderFile"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderFile" ADD CONSTRAINT "OrderFile_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "FileAccessLog" ADD CONSTRAINT "FileAccessLog_fileId_fkey" FOREIGN KEY ("fileId") REFERENCES "OrderFile"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Estimate" ADD CONSTRAINT "Estimate_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Message" ADD CONSTRAINT "Message_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Message" ADD CONSTRAINT "Message_fromUserId_fkey" FOREIGN KEY ("fromUserId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Rating" ADD CONSTRAINT "Rating_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Rating" ADD CONSTRAINT "Rating_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Rating" ADD CONSTRAINT "Rating_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LessonPackage" ADD CONSTRAINT "LessonPackage_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Payment" ADD CONSTRAINT "Payment_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OperatorTask" ADD CONSTRAINT "OperatorTask_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AgencyCommission" ADD CONSTRAINT "AgencyCommission_agencyId_fkey" FOREIGN KEY ("agencyId") REFERENCES "Agency"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AgencyCommission" ADD CONSTRAINT "AgencyCommission_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PromoCode" ADD CONSTRAINT "PromoCode_agencyId_fkey" FOREIGN KEY ("agencyId") REFERENCES "Agency"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PromoCodeUse" ADD CONSTRAINT "PromoCodeUse_promoId_fkey" FOREIGN KEY ("promoId") REFERENCES "PromoCode"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PromoCodeUse" ADD CONSTRAINT "PromoCodeUse_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Subscription" ADD CONSTRAINT "Subscription_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CardUsage" ADD CONSTRAINT "CardUsage_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CardUsage" ADD CONSTRAINT "CardUsage_partnerId_fkey" FOREIGN KEY ("partnerId") REFERENCES "Partner"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Payout" ADD CONSTRAINT "Payout_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "Provider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Payout" ADD CONSTRAINT "Payout_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "ProviderCompany"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AdminSession" ADD CONSTRAINT "AdminSession_adminUserId_fkey" FOREIGN KEY ("adminUserId") REFERENCES "AdminUser"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "AuditLog" ADD CONSTRAINT "AuditLog_adminUserId_fkey" FOREIGN KEY ("adminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL 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"
+749
View File
@@ -0,0 +1,749 @@
// Barev Club — модель данных (PRD, раздел 7).
// Схема заложена сразу целиком; часть сущностей используется начиная с Этапов 2–3.
generator client {
provider = "prisma-client"
output = "../src/generated/prisma"
moduleFormat = "esm"
}
datasource db {
provider = "postgresql"
}
// ───────────── Справочные перечисления ─────────────
enum Locale {
ru
en
zh
hy
es
}
/// Направление (раздел 2)
enum CategoryCode {
HOME
DOCS
LEARNING
PERKS
}
/// Сценарий заказа — определяет state machine и шаги исполнителя
enum ServiceFlow {
CLEANING
REPAIR
TRANSFER
LANGUAGE
CAREER
DOCUMENTS
}
enum ProviderRole {
CLEANER
HANDYMAN
DRIVER
TEACHER
CAREER_CONSULTANT
DOCS_AGENT
}
/// Все статусы всех сценариев. Допустимые переходы — в packages/core (flows).
enum OrderStatus {
NEW
ASSIGNED
EN_ROUTE
CLEANING
ON_SITE
ESTIMATE_APPROVED
REPAIRING
DRIVER_WAITING
RIDE_IN_PROGRESS
ENROLLED
LESSON_SCHEDULED
LESSON_IN_PROGRESS
LESSON_DONE
CONSULTANT_ASSIGNED
RESUME_RECEIVED
MEETING
PLAN_SENT
DOCS_REVIEW
NEEDS_MORE_DOCS
SUBMITTED
DONE
COMPLETED
CANCELLED
}
enum ActorType {
CLIENT
PROVIDER
DISPATCHER
ADMIN
SYSTEM
}
enum OfferStatus {
OFFERED
ACCEPTED
DECLINED
EXPIRED
REVOKED
}
enum PaymentStatus {
UNPAID
PAID
REFUNDED
}
enum RateType {
FIXED
PERCENT
}
enum AdminRole {
OWNER
OPERATOR
}
enum SubscriptionPlan {
BAREV_PLUS
}
enum SubscriptionSource {
AGENCY_GIFT
MANUAL
PAID
}
enum SubscriptionStatus {
ACTIVE
EXPIRED
CANCELLED
}
enum PromoKind {
AGENCY_GIFT
DISCOUNT
}
enum FileKind {
PHOTO_REPORT
CLIENT_PHOTO
DOCUMENT_SCAN
RESUME
OTHER
}
enum EstimateStatus {
PENDING
ACCEPTED
REJECTED
}
enum TaskStatus {
OPEN
DONE
}
enum PayoutStatus {
DRAFT
APPROVED
PAID
}
// ───────────── Пользователи ─────────────
/// Пользователь Telegram. Один и тот же человек может быть клиентом и исполнителем.
model User {
id String @id @default(cuid())
telegramId BigInt @unique
username String?
firstName String?
lastName String?
phone String?
locale Locale @default(ru)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
client Client?
provider Provider?
dispatcher CompanyDispatcher[]
messages Message[]
}
model Client {
id String @id @default(cuid())
userId String @unique
user User @relation(fields: [userId], references: [id])
consentAt DateTime?
consentVersion String?
agencyId String?
agency Agency? @relation(fields: [agencyId], references: [id])
agencyAttachedAt DateTime?
/// источник: deeplink | manual_code | organic
source String @default("organic")
utm Json?
/// подарочный месяц уже использован (один раз на клиента)
giftUsedAt DateTime?
/// короткий код клубной карты (Этап 3)
cardCode String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
orders Order[]
subscriptions Subscription[]
ratings Rating[]
cardUsages CardUsage[]
promoUses PromoCodeUse[]
@@index([agencyId])
}
// ───────────── Исполнители ─────────────
model ProviderCompany {
id String @id @default(cuid())
name String
/// какие роли исполнителей есть в компании (какие услуги она берёт)
roles ProviderRole[]
/// договорная ставка компании
rateType RateType @default(PERCENT)
rateValue Int @default(70)
contactPhone String?
inviteCode String @unique
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
employees Provider[]
dispatchers CompanyDispatcher[]
orders Order[]
offers OrderOffer[]
payouts Payout[]
}
model CompanyDispatcher {
id String @id @default(cuid())
companyId String
company ProviderCompany @relation(fields: [companyId], references: [id])
userId String
user User @relation(fields: [userId], references: [id])
createdAt DateTime @default(now())
@@unique([companyId, userId])
}
model Provider {
id String @id @default(cuid())
/// заполняется, когда исполнитель открыл бота по инвайт-коду
userId String? @unique
user User? @relation(fields: [userId], references: [id])
name String
phone String?
role ProviderRole
companyId String?
company ProviderCompany? @relation(fields: [companyId], references: [id])
languages Locale[]
/// ставка фрилансера; для сотрудников компании не используется
rateType RateType @default(PERCENT)
rateValue Int @default(70)
ratingSum Int @default(0)
ratingCount Int @default(0)
onShift Boolean @default(false)
active Boolean @default(true)
inviteCode String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
districts ProviderDistrict[]
schedule ProviderSchedule[]
orders Order[]
offers OrderOffer[]
ratings Rating[]
payouts Payout[]
@@index([role, active])
}
model District {
id String @id @default(cuid())
code String @unique
sortOrder Int @default(0)
providers ProviderDistrict[]
orders Order[]
}
model ProviderDistrict {
providerId String
provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
districtId String
district District @relation(fields: [districtId], references: [id], onDelete: Cascade)
@@id([providerId, districtId])
}
/// Недельный график: 0 = понедельник … 6 = воскресенье, минуты от полуночи по Asia/Yerevan
model ProviderSchedule {
id String @id @default(cuid())
providerId String
provider Provider @relation(fields: [providerId], references: [id], onDelete: Cascade)
weekday Int
startMinute Int
endMinute Int
@@index([providerId, weekday])
}
// ───────────── Каталог ─────────────
model Category {
id String @id @default(cuid())
code CategoryCode @unique
sortOrder Int @default(0)
/// скидка Barev+ на направление, %
barevPlusDiscountPct Int @default(0)
active Boolean @default(true)
services Service[]
}
model Service {
id String @id @default(cuid())
/// код услуги; тексты берутся из i18n по ключу services.<code>.*
code String @unique
categoryId String
category Category @relation(fields: [categoryId], references: [id])
flow ServiceFlow
providerRole ProviderRole
/// правила цены (см. packages/core/src/pricing)
pricing Json
/// описание параметров формы заказа (см. packages/core/src/params)
paramsSchema Json
/// окно записи и шаг слотов (см. packages/core/src/slots)
slotConfig Json
/// удержание при поздней отмене, %
lateCancelFeePct Int @default(30)
isOnline Boolean @default(false)
active Boolean @default(true)
sortOrder Int @default(0)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
checklist ChecklistItem[]
orders Order[]
}
/// Шаблон пункта чек-листа услуги
model ChecklistItem {
id String @id @default(cuid())
serviceId String
service Service @relation(fields: [serviceId], references: [id], onDelete: Cascade)
/// код пункта; подпись — i18n checklist.<code>
code String
requiresPhoto Boolean @default(false)
sortOrder Int @default(0)
@@unique([serviceId, code])
}
model PaymentMethod {
id String @id @default(cuid())
code String @unique
/// { ru, en, zh, hy, es }
name Json
active Boolean @default(true)
sortOrder Int @default(0)
orders Order[]
}
// ───────────── Заказы ─────────────
model Order {
id String @id @default(cuid())
number Int @unique @default(autoincrement())
clientId String
client Client @relation(fields: [clientId], references: [id])
serviceId String
service Service @relation(fields: [serviceId], references: [id])
params Json
scheduledAt DateTime
durationMinutes Int
isOnline Boolean @default(false)
addressText String?
districtId String?
district District? @relation(fields: [districtId], references: [id])
lat Float?
lng Float?
note String?
/// язык клиента на момент заказа
locale Locale
basePrice Int
discountPct Int @default(0)
discountAmount Int @default(0)
totalPrice Int
status OrderStatus @default(NEW)
providerId String?
provider Provider? @relation(fields: [providerId], references: [id])
/// заказ передан компании; пока providerId пуст — «Назначен на компанию»
companyId String?
company ProviderCompany? @relation(fields: [companyId], references: [id])
paymentMethodId String?
paymentMethod PaymentMethod? @relation(fields: [paymentMethodId], references: [id])
paymentStatus PaymentStatus @default(UNPAID)
paidAt DateTime?
cancelledAt DateTime?
cancelFee Int @default(0)
cancelReason String?
completedAt DateTime?
repeatOfId String?
reminded24hAt DateTime?
reminded1hAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
history OrderStatusHistory[]
checklist OrderChecklistItem[]
offers OrderOffer[]
files OrderFile[]
messages Message[]
estimates Estimate[]
rating Rating?
lessons LessonPackage?
payments Payment[]
commission AgencyCommission?
tasks OperatorTask[]
@@index([status])
@@index([scheduledAt])
@@index([clientId])
@@index([providerId, scheduledAt])
}
model OrderStatusHistory {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
fromStatus OrderStatus?
toStatus OrderStatus
/// событие без смены статуса (например «Я на месте») или код действия
event String?
actorType ActorType
actorId String?
comment String?
createdAt DateTime @default(now())
@@index([orderId, createdAt])
}
/// Предложение заказа исполнителю или компании (принять / отклонить с таймаутом)
model OrderOffer {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
providerId String?
provider Provider? @relation(fields: [providerId], references: [id])
companyId String?
company ProviderCompany? @relation(fields: [companyId], references: [id])
status OfferStatus @default(OFFERED)
expiresAt DateTime?
respondedAt DateTime?
createdById String?
createdAt DateTime @default(now())
@@index([orderId, status])
@@index([providerId, status])
@@index([companyId, status])
}
model OrderChecklistItem {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
code String
requiresPhoto Boolean @default(false)
sortOrder Int @default(0)
checkedAt DateTime?
fileId String? @unique
file OrderFile? @relation(fields: [fileId], references: [id])
@@unique([orderId, code])
}
model OrderFile {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
kind FileKind
storageKey String @unique
mime String
size Int
uploadedBy ActorType
uploadedById String?
/// дата автоудаления (документы — через N дней после завершения)
deleteAfter DateTime?
deletedAt DateTime?
createdAt DateTime @default(now())
checklistItem OrderChecklistItem?
accessLog FileAccessLog[]
}
model FileAccessLog {
id String @id @default(cuid())
fileId String
file OrderFile @relation(fields: [fileId], references: [id], onDelete: Cascade)
actorType ActorType
actorId String?
createdAt DateTime @default(now())
}
/// Смета ремонта (Этап 2)
model Estimate {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
items Json
total Int
status EstimateStatus @default(PENDING)
respondedAt DateTime?
createdAt DateTime @default(now())
}
/// Чат клиент ↔ исполнитель через бота (Этап 2)
model Message {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
fromType ActorType
fromUserId String?
fromUser User? @relation(fields: [fromUserId], references: [id])
text String?
fileId String?
createdAt DateTime @default(now())
@@index([orderId, createdAt])
}
model Rating {
id String @id @default(cuid())
orderId String @unique
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
clientId String
client Client @relation(fields: [clientId], references: [id])
providerId String?
provider Provider? @relation(fields: [providerId], references: [id])
score Int
comment String?
createdAt DateTime @default(now())
}
/// Пакет уроков (Этап 2)
model LessonPackage {
id String @id @default(cuid())
orderId String @unique
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
total Int
completed Int @default(0)
}
model Payment {
id String @id @default(cuid())
orderId String
order Order @relation(fields: [orderId], references: [id], onDelete: Cascade)
/// manual | <будущий провайдер>
provider String
amount Int
status PaymentStatus
externalId String?
markedById String?
createdAt DateTime @default(now())
}
model OperatorTask {
id String @id @default(cuid())
type String
orderId String?
order Order? @relation(fields: [orderId], references: [id], onDelete: Cascade)
status TaskStatus @default(OPEN)
note String?
createdAt DateTime @default(now())
doneAt DateTime?
}
// ───────────── Агентства, промокоды, членство ─────────────
model Agency {
id String @id @default(cuid())
/// код для deep-link и ручного ввода (латиница/цифры)
code String @unique
name String
/// вознаграждение, % от оплаченных заказов клиентов агентства
commissionPct Int @default(10)
contact String?
active Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
clients Client[]
commissions AgencyCommission[]
promoCodes PromoCode[]
}
model AgencyCommission {
id String @id @default(cuid())
agencyId String
agency Agency @relation(fields: [agencyId], references: [id])
orderId String @unique
order Order @relation(fields: [orderId], references: [id])
pct Int
amount Int
/// YYYY-MM
period String
createdAt DateTime @default(now())
@@index([agencyId, period])
}
model PromoCode {
id String @id @default(cuid())
code String @unique
kind PromoKind
agencyId String?
agency Agency? @relation(fields: [agencyId], references: [id])
discountPct Int?
maxUses Int?
usedCount Int @default(0)
validFrom DateTime?
validUntil DateTime?
active Boolean @default(true)
createdAt DateTime @default(now())
uses PromoCodeUse[]
}
model PromoCodeUse {
id String @id @default(cuid())
promoId String
promo PromoCode @relation(fields: [promoId], references: [id])
clientId String
client Client @relation(fields: [clientId], references: [id])
orderId String?
createdAt DateTime @default(now())
@@unique([promoId, clientId])
}
model Subscription {
id String @id @default(cuid())
clientId String
client Client @relation(fields: [clientId], references: [id])
plan SubscriptionPlan @default(BAREV_PLUS)
source SubscriptionSource
status SubscriptionStatus @default(ACTIVE)
startsAt DateTime
endsAt DateTime
remindedAt DateTime?
createdAt DateTime @default(now())
@@index([clientId, endsAt])
}
// ───────────── Партнёры и клубная карта (Этап 3) ─────────────
model Partner {
id String @id @default(cuid())
/// cafe | restaurant | other
category String
name String
/// { ru, en, ... }
offer Json
terms Json?
address String?
active Boolean @default(true)
createdAt DateTime @default(now())
usages CardUsage[]
}
model CardUsage {
id String @id @default(cuid())
clientId String
client Client @relation(fields: [clientId], references: [id])
partnerId String?
partner Partner? @relation(fields: [partnerId], references: [id])
createdAt DateTime @default(now())
}
// ───────────── Финансы ─────────────
model Payout {
id String @id @default(cuid())
providerId String?
provider Provider? @relation(fields: [providerId], references: [id])
companyId String?
company ProviderCompany? @relation(fields: [companyId], references: [id])
periodStart DateTime
periodEnd DateTime
amount Int
status PayoutStatus @default(DRAFT)
createdAt DateTime @default(now())
}
// ───────────── Админка ─────────────
model AdminUser {
id String @id @default(cuid())
email String @unique
name String
passwordHash String
role AdminRole @default(OPERATOR)
totpSecret String?
totpEnabled Boolean @default(false)
active Boolean @default(true)
createdAt DateTime @default(now())
sessions AdminSession[]
audit AuditLog[]
}
model AdminSession {
id String @id @default(cuid())
adminUserId String
adminUser AdminUser @relation(fields: [adminUserId], references: [id], onDelete: Cascade)
/// sha256 токена сессии
tokenHash String @unique
/// сессия прошла 2FA
verified Boolean @default(false)
expiresAt DateTime
createdAt DateTime @default(now())
}
model AuditLog {
id String @id @default(cuid())
adminUserId String?
adminUser AdminUser? @relation(fields: [adminUserId], references: [id])
action String
entity String
entityId String?
data Json?
createdAt DateTime @default(now())
@@index([entity, entityId])
}
/// Настройки (ключ → значение). Список ключей и дефолты — packages/core/src/settings.ts
model Setting {
key String @id
value Json
updatedAt DateTime @updatedAt
}
/// Короткий токен для deep-link с лендинга, когда payload не влезает в 64 символа (UTM)
model StartToken {
id String @id
locale Locale?
agency String?
utm Json?
createdAt DateTime @default(now())
}