mirror of
https://git.meshkee.com/BaloutPastry/backend.git
synced 2026-08-11 22:31:00 +04:30
Add SMS OTP auth, discounts, customer orders, and category slugs.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
58380ab81d
commit
d09eca8702
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "OrderItem" ALTER COLUMN "quantity" SET DATA TYPE DOUBLE PRECISION;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Order" ALTER COLUMN "itemCount" SET DATA TYPE DOUBLE PRECISION;
|
||||
@@ -0,0 +1,27 @@
|
||||
-- Renumber existing low order numbers to start at 1000, then seed the sequence.
|
||||
DO $$
|
||||
DECLARE
|
||||
seq_name text;
|
||||
seed bigint;
|
||||
r record;
|
||||
i integer := 0;
|
||||
BEGIN
|
||||
-- Move current numbers out of the way to avoid unique collisions
|
||||
UPDATE "Order" SET number = number + 1000000 WHERE number < 1000;
|
||||
|
||||
-- Assign contiguous numbers from 1000 for any that were shifted
|
||||
FOR r IN
|
||||
SELECT id FROM "Order" WHERE number >= 1000000 ORDER BY number ASC
|
||||
LOOP
|
||||
UPDATE "Order" SET number = 1000 + i WHERE id = r.id;
|
||||
i := i + 1;
|
||||
END LOOP;
|
||||
|
||||
seq_name := pg_get_serial_sequence('"Order"', 'number');
|
||||
IF seq_name IS NULL THEN
|
||||
RAISE EXCEPTION 'Order.number sequence not found';
|
||||
END IF;
|
||||
|
||||
SELECT GREATEST(999, COALESCE(MAX(number), 999)) INTO seed FROM "Order";
|
||||
PERFORM setval(seq_name, seed, true);
|
||||
END $$;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "Discount" (
|
||||
"id" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"categoryId" TEXT,
|
||||
"userId" TEXT NOT NULL,
|
||||
"minOrderAmount" INTEGER NOT NULL DEFAULT 0,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"percent" INTEGER NOT NULL,
|
||||
"maxValue" INTEGER NOT NULL,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"createdByAdminId" TEXT,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Discount_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Discount_code_key" ON "Discount"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Discount_userId_idx" ON "Discount"("userId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Discount_categoryId_idx" ON "Discount"("categoryId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Discount_active_expiresAt_idx" ON "Discount"("active", "expiresAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Discount" ADD CONSTRAINT "Discount_createdByAdminId_fkey" FOREIGN KEY ("createdByAdminId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Discount" ALTER COLUMN "userId" DROP NOT NULL;
|
||||
@@ -0,0 +1,38 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Category" ADD COLUMN "slug" TEXT;
|
||||
|
||||
-- Backfill from nameEn (latin slug). Keep unique with id suffix on collision.
|
||||
UPDATE "Category" AS c
|
||||
SET "slug" = lower(
|
||||
regexp_replace(
|
||||
regexp_replace(trim(c."nameEn"), '[^a-zA-Z0-9]+', '-', 'g'),
|
||||
'(^-+|-+$)',
|
||||
'',
|
||||
'g'
|
||||
)
|
||||
);
|
||||
|
||||
UPDATE "Category"
|
||||
SET "slug" = 'category'
|
||||
WHERE "slug" IS NULL OR "slug" = '';
|
||||
|
||||
UPDATE "Category" AS c
|
||||
SET "slug" = c."slug" || '-' || right(c."id", 4)
|
||||
WHERE c."id" IN (
|
||||
SELECT id FROM (
|
||||
SELECT id, ROW_NUMBER() OVER (PARTITION BY slug ORDER BY "createdAt", id) AS rn
|
||||
FROM "Category"
|
||||
) d
|
||||
WHERE rn > 1
|
||||
);
|
||||
|
||||
ALTER TABLE "Category" ALTER COLUMN "slug" SET NOT NULL;
|
||||
|
||||
-- Prefer readable Persian-romanized top-level slugs
|
||||
UPDATE "Category" SET "slug" = 'shirini' WHERE "nameFa" = 'شیرینی' AND "parentId" IS NULL;
|
||||
UPDATE "Category" SET "slug" = 'cake' WHERE "nameFa" = 'کیک' AND "parentId" IS NULL;
|
||||
UPDATE "Category" SET "slug" = 'shirini-tar' WHERE "nameFa" = 'شیرینی تر';
|
||||
UPDATE "Category" SET "slug" = 'shirini-khoshk' WHERE "nameFa" = 'شیرینی خشک';
|
||||
UPDATE "Category" SET "slug" = 'cream-cake' WHERE "nameFa" = 'کیک خامه';
|
||||
|
||||
CREATE UNIQUE INDEX "Category_slug_key" ON "Category"("slug");
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Order" ADD COLUMN "discountCode" TEXT;
|
||||
ALTER TABLE "Order" ADD COLUMN "discountAmount" INTEGER NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1,22 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "SmsOtpPurpose" AS ENUM ('register');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SmsOtp" (
|
||||
"id" TEXT NOT NULL,
|
||||
"cellNumber" TEXT NOT NULL,
|
||||
"purpose" "SmsOtpPurpose" NOT NULL,
|
||||
"codeHash" TEXT NOT NULL,
|
||||
"payload" JSONB,
|
||||
"attempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"expiresAt" TIMESTAMP(3) NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "SmsOtp_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "SmsOtp_cellNumber_purpose_idx" ON "SmsOtp"("cellNumber", "purpose");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "SmsOtp_expiresAt_idx" ON "SmsOtp"("expiresAt");
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "SmsOtpPurpose" ADD VALUE 'login';
|
||||
ALTER TYPE "SmsOtpPurpose" ADD VALUE 'resetPassword';
|
||||
@@ -1 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (e.g., Git)
|
||||
provider = "postgresql"
|
||||
|
||||
+51
-2
@@ -49,6 +49,8 @@ model User {
|
||||
refreshTokens RefreshToken[]
|
||||
addresses UserAddress[]
|
||||
orders Order[]
|
||||
discounts Discount[] @relation("DiscountAssignee")
|
||||
discountsCreated Discount[] @relation("DiscountCreatedBy")
|
||||
|
||||
@@index([role])
|
||||
@@index([disabled])
|
||||
@@ -80,6 +82,27 @@ model RefreshToken {
|
||||
@@index([tokenHash])
|
||||
}
|
||||
|
||||
enum SmsOtpPurpose {
|
||||
register
|
||||
login
|
||||
resetPassword
|
||||
}
|
||||
|
||||
model SmsOtp {
|
||||
id String @id @default(cuid())
|
||||
cellNumber String
|
||||
purpose SmsOtpPurpose
|
||||
codeHash String
|
||||
/// Pending registration fields (firstName, lastName, title, passwordHash)
|
||||
payload Json?
|
||||
attempts Int @default(0)
|
||||
expiresAt DateTime
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@index([cellNumber, purpose])
|
||||
@@index([expiresAt])
|
||||
}
|
||||
|
||||
model Flavor {
|
||||
id String @id @default(cuid())
|
||||
nameFa String
|
||||
@@ -94,6 +117,7 @@ model Category {
|
||||
id String @id @default(cuid())
|
||||
nameFa String
|
||||
nameEn String
|
||||
slug String @unique
|
||||
parentId String?
|
||||
parent Category? @relation("CategoryTree", fields: [parentId], references: [id], onDelete: Restrict)
|
||||
children Category[] @relation("CategoryTree")
|
||||
@@ -102,6 +126,7 @@ model Category {
|
||||
updatedAt DateTime @updatedAt
|
||||
optionBlocks CategoryOptionBlock[]
|
||||
products Product[]
|
||||
discounts Discount[]
|
||||
|
||||
@@index([parentId])
|
||||
}
|
||||
@@ -215,7 +240,9 @@ model Order {
|
||||
shippingAddressLine String?
|
||||
shippingLandline String?
|
||||
note String @default("")
|
||||
itemCount Int
|
||||
discountCode String?
|
||||
discountAmount Int @default(0)
|
||||
itemCount Float
|
||||
totalPrice Int
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
@@ -234,7 +261,7 @@ model OrderItem {
|
||||
productId String?
|
||||
product Product? @relation(fields: [productId], references: [id], onDelete: SetNull)
|
||||
nameFa String
|
||||
quantity Int
|
||||
quantity Float
|
||||
unitPrice Int
|
||||
sellUnit SellUnit
|
||||
sortOrder Int @default(0)
|
||||
@@ -254,3 +281,25 @@ model OrderItemOption {
|
||||
|
||||
@@index([orderItemId])
|
||||
}
|
||||
|
||||
model Discount {
|
||||
id String @id @default(cuid())
|
||||
code String @unique
|
||||
categoryId String?
|
||||
category Category? @relation(fields: [categoryId], references: [id], onDelete: SetNull)
|
||||
userId String?
|
||||
user User? @relation("DiscountAssignee", fields: [userId], references: [id], onDelete: Cascade)
|
||||
minOrderAmount Int @default(0)
|
||||
expiresAt DateTime
|
||||
percent Int
|
||||
maxValue Int
|
||||
active Boolean @default(true)
|
||||
createdByAdminId String?
|
||||
createdByAdmin User? @relation("DiscountCreatedBy", fields: [createdByAdminId], references: [id], onDelete: SetNull)
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@index([userId])
|
||||
@@index([categoryId])
|
||||
@@index([active, expiresAt])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user