Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
268 lines
12 KiB
SQL
268 lines
12 KiB
SQL
-- CreateEnum
|
||
CREATE TYPE "PointsKind" AS ENUM ('ORDER', 'BONUS');
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "PointsStatus" AS ENUM ('PENDING', 'ACTIVE', 'SPENT', 'EXPIRED', 'REVERSED');
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "PointsChannel" AS ENUM ('WEB', 'BOT', 'CSV', 'AUTO', 'CLIENT');
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "RedemptionStatus" AS ENUM ('REQUESTED', 'CONFIRMED', 'REJECTED', 'REVERSED');
|
||
|
||
-- CreateEnum
|
||
CREATE TYPE "SuggestionStatus" AS ENUM ('OPEN', 'ACCEPTED', 'REJECTED');
|
||
|
||
-- AlterTable
|
||
ALTER TABLE "Agency" ADD COLUMN "housewarmingDays" INTEGER NOT NULL DEFAULT 60,
|
||
ADD COLUMN "housewarmingPoints" INTEGER NOT NULL DEFAULT 0;
|
||
|
||
-- AlterTable
|
||
ALTER TABLE "Client" ADD COLUMN "pointsActive" INTEGER NOT NULL DEFAULT 0,
|
||
ADD COLUMN "pointsActivityAt" TIMESTAMP(3),
|
||
ADD COLUMN "pointsDebt" INTEGER NOT NULL DEFAULT 0,
|
||
ADD COLUMN "pointsPending" INTEGER NOT NULL DEFAULT 0,
|
||
ADD COLUMN "pointsWarn14At" TIMESTAMP(3),
|
||
ADD COLUMN "pointsWarn3At" TIMESTAMP(3),
|
||
ADD COLUMN "streakCount" INTEGER NOT NULL DEFAULT 0,
|
||
ADD COLUMN "streakLastAt" TIMESTAMP(3);
|
||
|
||
-- AlterTable
|
||
ALTER TABLE "Order" ADD COLUMN "pointsUsed" INTEGER NOT NULL DEFAULT 0;
|
||
|
||
-- AlterTable
|
||
ALTER TABLE "Service" ADD COLUMN "pointsCategoryId" TEXT;
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsCategory" (
|
||
"id" TEXT NOT NULL,
|
||
"code" TEXT NOT NULL,
|
||
"name" JSONB NOT NULL,
|
||
"rateBarev" INTEGER NOT NULL,
|
||
"ratePlus" INTEGER NOT NULL,
|
||
"rateElite" INTEGER NOT NULL,
|
||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||
|
||
CONSTRAINT "PointsCategory_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsBonusType" (
|
||
"id" TEXT NOT NULL,
|
||
"code" TEXT NOT NULL,
|
||
"name" JSONB NOT NULL,
|
||
"defaultAmount" INTEGER NOT NULL,
|
||
"ttlDays" INTEGER,
|
||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||
|
||
CONSTRAINT "PointsBonusType_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsAccrual" (
|
||
"id" TEXT NOT NULL,
|
||
"clientId" TEXT NOT NULL,
|
||
"kind" "PointsKind" NOT NULL,
|
||
"categoryId" TEXT,
|
||
"bonusTypeId" TEXT,
|
||
"orderId" TEXT,
|
||
"orderRef" TEXT,
|
||
"orderAmount" INTEGER,
|
||
"excludedAmount" INTEGER NOT NULL DEFAULT 0,
|
||
"paidWithPoints" INTEGER NOT NULL DEFAULT 0,
|
||
"tier" TEXT,
|
||
"ratePct" INTEGER,
|
||
"streakBonusPct" INTEGER NOT NULL DEFAULT 0,
|
||
"suggestedAmount" INTEGER,
|
||
"amount" INTEGER NOT NULL,
|
||
"remaining" INTEGER NOT NULL,
|
||
"expiredAmount" INTEGER NOT NULL DEFAULT 0,
|
||
"status" "PointsStatus" NOT NULL,
|
||
"serviceDate" TIMESTAMP(3) NOT NULL,
|
||
"availableAt" TIMESTAMP(3) NOT NULL,
|
||
"expiresAt" TIMESTAMP(3),
|
||
"reason" TEXT,
|
||
"commentForClient" TEXT,
|
||
"createdById" TEXT,
|
||
"channel" "PointsChannel" NOT NULL,
|
||
"idempotencyKey" TEXT,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
CONSTRAINT "PointsAccrual_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsRedemption" (
|
||
"id" TEXT NOT NULL,
|
||
"clientId" TEXT NOT NULL,
|
||
"orderId" TEXT,
|
||
"orderRef" TEXT,
|
||
"orderAmount" INTEGER,
|
||
"amount" INTEGER NOT NULL,
|
||
"status" "RedemptionStatus" NOT NULL,
|
||
"channel" "PointsChannel" NOT NULL,
|
||
"note" TEXT,
|
||
"requestedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
"expiresAt" TIMESTAMP(3),
|
||
"confirmedById" TEXT,
|
||
"confirmedAt" TIMESTAMP(3),
|
||
"reason" TEXT,
|
||
"idempotencyKey" TEXT,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
CONSTRAINT "PointsRedemption_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsRedemptionItem" (
|
||
"id" TEXT NOT NULL,
|
||
"redemptionId" TEXT NOT NULL,
|
||
"accrualId" TEXT NOT NULL,
|
||
"amount" INTEGER NOT NULL,
|
||
|
||
CONSTRAINT "PointsRedemptionItem_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsReversal" (
|
||
"id" TEXT NOT NULL,
|
||
"targetType" TEXT NOT NULL,
|
||
"targetId" TEXT NOT NULL,
|
||
"clientId" TEXT NOT NULL,
|
||
"amount" INTEGER NOT NULL,
|
||
"debt" INTEGER NOT NULL DEFAULT 0,
|
||
"createdById" TEXT,
|
||
"channel" "PointsChannel" NOT NULL,
|
||
"reason" TEXT NOT NULL,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
CONSTRAINT "PointsReversal_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "PointsSuggestion" (
|
||
"id" TEXT NOT NULL,
|
||
"clientId" TEXT NOT NULL,
|
||
"mechanic" TEXT NOT NULL,
|
||
"dedupeKey" TEXT NOT NULL,
|
||
"payload" JSONB NOT NULL,
|
||
"suggestedAmount" INTEGER NOT NULL,
|
||
"status" "SuggestionStatus" NOT NULL DEFAULT 'OPEN',
|
||
"resolvedById" TEXT,
|
||
"resolvedAt" TIMESTAMP(3),
|
||
"reason" TEXT,
|
||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
CONSTRAINT "PointsSuggestion_pkey" PRIMARY KEY ("id")
|
||
);
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "PointsCategory_code_key" ON "PointsCategory"("code");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "PointsBonusType_code_key" ON "PointsBonusType"("code");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "PointsAccrual_idempotencyKey_key" ON "PointsAccrual"("idempotencyKey");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsAccrual_clientId_status_idx" ON "PointsAccrual"("clientId", "status");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsAccrual_status_availableAt_idx" ON "PointsAccrual"("status", "availableAt");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsAccrual_orderId_idx" ON "PointsAccrual"("orderId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsAccrual_createdAt_idx" ON "PointsAccrual"("createdAt");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "PointsRedemption_idempotencyKey_key" ON "PointsRedemption"("idempotencyKey");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsRedemption_clientId_status_idx" ON "PointsRedemption"("clientId", "status");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsRedemption_status_requestedAt_idx" ON "PointsRedemption"("status", "requestedAt");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsRedemptionItem_accrualId_idx" ON "PointsRedemptionItem"("accrualId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsReversal_targetType_targetId_idx" ON "PointsReversal"("targetType", "targetId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsReversal_clientId_idx" ON "PointsReversal"("clientId");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsReversal_createdAt_idx" ON "PointsReversal"("createdAt");
|
||
|
||
-- CreateIndex
|
||
CREATE UNIQUE INDEX "PointsSuggestion_dedupeKey_key" ON "PointsSuggestion"("dedupeKey");
|
||
|
||
-- CreateIndex
|
||
CREATE INDEX "PointsSuggestion_status_createdAt_idx" ON "PointsSuggestion"("status", "createdAt");
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "Service" ADD CONSTRAINT "Service_pointsCategoryId_fkey" FOREIGN KEY ("pointsCategoryId") REFERENCES "PointsCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsAccrual" ADD CONSTRAINT "PointsAccrual_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsAccrual" ADD CONSTRAINT "PointsAccrual_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "PointsCategory"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsAccrual" ADD CONSTRAINT "PointsAccrual_bonusTypeId_fkey" FOREIGN KEY ("bonusTypeId") REFERENCES "PointsBonusType"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsAccrual" ADD CONSTRAINT "PointsAccrual_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsRedemption" ADD CONSTRAINT "PointsRedemption_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsRedemption" ADD CONSTRAINT "PointsRedemption_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsRedemptionItem" ADD CONSTRAINT "PointsRedemptionItem_redemptionId_fkey" FOREIGN KEY ("redemptionId") REFERENCES "PointsRedemption"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsRedemptionItem" ADD CONSTRAINT "PointsRedemptionItem_accrualId_fkey" FOREIGN KEY ("accrualId") REFERENCES "PointsAccrual"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
-- AddForeignKey
|
||
ALTER TABLE "PointsSuggestion" ADD CONSTRAINT "PointsSuggestion_clientId_fkey" FOREIGN KEY ("clientId") REFERENCES "Client"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||
|
||
|
||
-- номер заказа уникален среди неотменённых начислений; автоначисление — один раз на заказ
|
||
CREATE UNIQUE INDEX "PointsAccrual_orderRef_live_key" ON "PointsAccrual"("orderRef") WHERE "orderRef" IS NOT NULL AND "status" <> 'REVERSED';
|
||
CREATE UNIQUE INDEX "PointsAccrual_orderId_live_key" ON "PointsAccrual"("orderId") WHERE "orderId" IS NOT NULL AND "status" <> 'REVERSED';
|
||
|
||
-- категории кэшбека (ставки по ТЗ; Elite = Barev+ +2)
|
||
INSERT INTO "PointsCategory" ("id", "code", "name", "rateBarev", "ratePlus", "rateElite", "sortOrder") VALUES
|
||
('pc_cleaning', 'cleaning', '{"ru":"Клининг","en":"Cleaning","zh":"保洁","hy":"Մաքրում","es":"Limpieza"}', 5, 8, 10, 1),
|
||
('pc_documents', 'documents', '{"ru":"Документы","en":"Documents","zh":"证件","hy":"Փաստաթղթեր","es":"Documentos"}', 5, 10, 12, 2),
|
||
('pc_learning', 'learning', '{"ru":"Обучение и карьера","en":"Learning and career","zh":"学习与职业","hy":"Ուսում և կարիերա","es":"Formación y carrera"}', 3, 4, 6, 3),
|
||
('pc_repair', 'repair', '{"ru":"Ремонт","en":"Repairs","zh":"维修","hy":"Վերանորոգում","es":"Reparaciones"}', 2, 4, 6, 4),
|
||
('pc_transfer', 'transfer', '{"ru":"Трансфер","en":"Transfer","zh":"接送","hy":"Տրանսֆեր","es":"Traslado"}', 2, 4, 6, 5);
|
||
|
||
UPDATE "Service" SET "pointsCategoryId" = 'pc_cleaning' WHERE "code" = 'cleaning';
|
||
UPDATE "Service" SET "pointsCategoryId" = 'pc_repair' WHERE "code" = 'repair';
|
||
UPDATE "Service" SET "pointsCategoryId" = 'pc_transfer' WHERE "code" = 'transfer';
|
||
UPDATE "Service" s SET "pointsCategoryId" = 'pc_documents' FROM "Category" c WHERE c."id" = s."categoryId" AND c."code" = 'DOCS';
|
||
UPDATE "Service" s SET "pointsCategoryId" = 'pc_learning' FROM "Category" c WHERE c."id" = s."categoryId" AND c."code" = 'LEARNING';
|
||
|
||
-- типы бонусов
|
||
INSERT INTO "PointsBonusType" ("id", "code", "name", "defaultAmount", "ttlDays", "sortOrder") VALUES
|
||
('pb_housewarming', 'housewarming', '{"ru":"Новоселье","en":"Housewarming","zh":"乔迁之喜","hy":"Բնակարանամուտ","es":"Mudanza"}', 5000, 60, 1),
|
||
('pb_route', 'first_month_route', '{"ru":"Маршрут первого месяца","en":"First-month route","zh":"首月路线","hy":"Առաջին ամսվա երթուղի","es":"Ruta del primer mes"}', 3000, NULL, 2),
|
||
('pb_ref_inviter', 'referral_inviter', '{"ru":"Пригласил друга","en":"Invited a friend","zh":"邀请好友","hy":"Հրավիրել է ընկերոջ","es":"Invitó a un amigo"}', 3000, NULL, 3),
|
||
('pb_ref_invitee', 'referral_invitee', '{"ru":"По приглашению друга","en":"Invited by a friend","zh":"好友邀请","hy":"Ընկերոջ հրավերով","es":"Invitado por un amigo"}', 3000, NULL, 4),
|
||
('pb_photo', 'photo_review', '{"ru":"Отзыв с фото","en":"Review with a photo","zh":"带图评价","hy":"Կարծիք լուսանկարով","es":"Reseña con foto"}', 500, NULL, 5),
|
||
('pb_lease', 'lease_renewal', '{"ru":"Продление аренды","en":"Lease renewal","zh":"续租","hy":"Վարձակալության երկարաձգում","es":"Renovación del alquiler"}', 2000, 60, 6),
|
||
('pb_complaint', 'complaint', '{"ru":"Компенсация","en":"Compensation","zh":"补偿","hy":"Փոխհատուցում","es":"Compensación"}', 1000, NULL, 7),
|
||
('pb_promo', 'promo', '{"ru":"Промо","en":"Promo","zh":"促销","hy":"Պրոմո","es":"Promoción"}', 1000, 30, 8);
|