Merge branch 'dev'

# Conflicts:
#	apps/admin-panel/dist/index.html
#	apps/admin-panel/nginx.conf
#	apps/admin-panel/vite.config.ts
#	apps/api/release/app.js
This commit is contained in:
Montech
2026-09-16 15:45:23 +03:30
435 changed files with 9419 additions and 4450 deletions
+59
View File
@@ -0,0 +1,59 @@
// Generates distinct SVG placeholder images per product category, so the 200
// mock products render varied artwork instead of one shared no-items.png icon.
// Not photography — an honest stand-in, styled to the app's own gold/cream
// palette, with a distinct glyph + background tint per category so the grid
// reads as varied rather than one repeated tile.
const fs = require('fs');
const path = require('path');
const OUT_DIR = path.join(__dirname, '../../user-app/public/img/products');
fs.mkdirSync(OUT_DIR, { recursive: true });
// category slug -> [glyph path (viewBox 0 0 64 64), accent color]
const GLYPHS = {
rings: {
accent: '#B08D57',
path: '<circle cx="32" cy="40" r="14" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="20" r="6" fill="currentColor"/>',
},
bracelets: {
accent: '#8D6A46',
path: '<ellipse cx="32" cy="32" rx="22" ry="14" fill="none" stroke="currentColor" stroke-width="4"/><ellipse cx="32" cy="32" rx="14" ry="8" fill="none" stroke="currentColor" stroke-width="2" opacity="0.5"/>',
},
necklaces: {
accent: '#C9A24B',
path: '<path d="M14 14 C14 34 24 44 32 44 C40 44 50 34 50 14" fill="none" stroke="currentColor" stroke-width="3"/><path d="M32 44 L32 52 L26 58 L38 58 Z" fill="currentColor"/>',
},
earrings: {
accent: '#A67C52',
path: '<circle cx="32" cy="16" r="5" fill="currentColor"/><path d="M32 21 L32 34" stroke="currentColor" stroke-width="3"/><circle cx="32" cy="42" r="10" fill="none" stroke="currentColor" stroke-width="3"/>',
},
coins: {
accent: '#D4AF37',
path: '<circle cx="32" cy="32" r="22" fill="none" stroke="currentColor" stroke-width="4"/><circle cx="32" cy="32" r="14" fill="none" stroke="currentColor" stroke-width="2" opacity="0.6"/><text x="32" y="38" font-size="14" text-anchor="middle" fill="currentColor" font-family="sans-serif">Au</text>',
},
};
// A handful of warm background tints so 200 tiles don't look identical even
// within one category.
const BG_TINTS = ['#F6F3EF', '#F3EADC', '#FAF5EC', '#F0E6D6', '#F8F1E6', '#EFE4D3'];
function svgFor(categorySlug, variantIndex) {
const g = GLYPHS[categorySlug] || GLYPHS.rings;
const bg = BG_TINTS[variantIndex % BG_TINTS.length];
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="480" height="480">
<rect width="64" height="64" fill="${bg}"/>
<g transform="translate(0,0)" color="${g.accent}" opacity="0.92">
${g.path}
</g>
</svg>`;
}
let written = 0;
for (const slug of Object.keys(GLYPHS)) {
for (let i = 0; i < 6; i++) {
const file = path.join(OUT_DIR, `${slug}-${i}.svg`);
fs.writeFileSync(file, svgFor(slug, i));
written++;
}
}
console.log(`wrote ${written} placeholder SVGs to ${OUT_DIR}`);
+118
View File
@@ -0,0 +1,118 @@
const fs = require('fs');
const path = require('path');
const seedPath = path.join(__dirname, 'seed.ts');
let content = fs.readFileSync(seedPath, 'utf-8');
const marker = ' imageCount += 1;\n }\n\n // \u2500\u2500 Featured collection';
if (!content.includes(marker)) {
console.error('MARKER NOT FOUND');
process.exit(1);
}
const insertion = ` imageCount += 1;
}
// \u2500\u2500 200 mock products for a realistic-looking catalogue \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
// These are generated, not hand-authored like the 5 above (whose ids are
// referenced elsewhere in this seed, e.g. by orders \u2014 left untouched).
// Images: real product photography does not exist in this project, so each
// mock product cycles through 6 generated SVG placeholders per category
// (apps/user-app/public/img/products/<slug>-N.svg), giving the catalogue
// grid genuine visual variety instead of one repeated icon.
const MOCK_CATEGORIES = [
{ slug: 'rings', id: ringsCategory.id, nouns: ['انگشتر نگین‌دار', 'انگشتر سولیتر', 'انگشتر آویزدار', 'انگشتر پهن', 'انگشتر ظریف', 'انگشتر رینگی'] },
{ slug: 'bracelets', id: braceletsCategory.id, nouns: ['دستبند زنجیری', 'دستبند پاندول', 'دستبند بافت', 'دستبند تنیس', 'دستبند پلاک‌دار', 'دستبند چرمی طلاکوب'] },
{ slug: 'necklaces', id: necklacesCategory.id, nouns: ['گردنبند آویز قلب', 'گردنبند ماه و ستاره', 'گردنبند ظریف', 'گردنبند رولو', 'گردنبند صلیب', 'گردنبند اسم'] },
{ slug: 'earrings', id: earringsCategory.id, nouns: ['گوشواره حلقه‌ای', 'گوشواره آویز', 'گوشواره میخی', 'گوشواره پروانه‌ای', 'گوشواره مرواریدی', 'گوشواره هندسی'] },
{ slug: 'coins', id: coinsCategory.id, nouns: ['ربع سکه بهار آزادی', 'نیم سکه بهار آزادی', 'سکه امامی', 'سکه پارسیان', 'شمش طلای ۱۰ گرمی', 'شمش طلای ۵ گرمی'] },
];
const MOCK_ADJECTIVES = ['کلاسیک', 'مدرن', 'مینیمال', 'لوکس', 'دست‌ساز', 'ظریف', 'خاص', 'شیک', 'سنتی', 'رزگلد'];
const MOCK_BRANDS = [brandMontzar.id, brandArtisan.id];
const MOCK_PRODUCT_COUNT = 200;
let mockImageCount = 0;
for (let i = 0; i < MOCK_PRODUCT_COUNT; i++) {
const cat = MOCK_CATEGORIES[i % MOCK_CATEGORIES.length];
const noun = cat.nouns[i % cat.nouns.length];
const adjective = MOCK_ADJECTIVES[i % MOCK_ADJECTIVES.length];
const brandId = MOCK_BRANDS[i % MOCK_BRANDS.length];
const productId = \`mock-product-\${i + 1}\`;
const title = \`\${noun} \${adjective} \${i + 1}\`;
// Deterministic but varied numeric fields \u2014 no randomness, so a re-seed
// is stable and diffable.
const baseWeight = 1.5 + ((i * 37) % 250) / 20; // ~1.5g .. ~14g
const wagePercent = (6 + ((i * 13) % 12)).toFixed(1); // 6.0% .. 17.9%
const qty = (i * 7) % 15; // 0..14, so some are genuinely out of stock
const isPreOrder = i % 9 === 0;
const product = await prisma.product.upsert({
where: { id: productId },
update: {},
create: {
id: productId,
title,
description: \`\${noun} با طراحی \${adjective}، مناسب استفاده روزمره یا هدیه.\`,
categoryId: cat.id,
brandId,
productCode: \`P-\${String(i + 1).padStart(4, '0')}\`,
},
});
const variantId = \`\${productId}-variant-1\`;
await prisma.productVariant.upsert({
where: { id: variantId },
update: {},
create: {
id: variantId,
productId: product.id,
title: 'استاندارد',
sku: \`MOCK-\${String(i + 1).padStart(4, '0')}\`,
rashWeightGrams: baseWeight.toFixed(3),
defaultWagePercent: wagePercent,
qty,
isPreOrder,
},
});
// A second variant on ~1 in 4 products, so the "\u062a\u0646\u0648\u0639 \u0648\u0632\u0646\u06cc" chip row has
// something real to show on more than the 5 hand-authored products.
if (i % 4 === 0) {
const variant2Id = \`\${productId}-variant-2\`;
await prisma.productVariant.upsert({
where: { id: variant2Id },
update: {},
create: {
id: variant2Id,
productId: product.id,
title: 'سایز بزرگ',
sku: \`MOCK-\${String(i + 1).padStart(4, '0')}-L\`,
rashWeightGrams: (baseWeight * 1.35).toFixed(3),
defaultWagePercent: wagePercent,
qty: (qty + 3) % 15,
isPreOrder,
},
});
}
const imgIndex = i % 6;
const imageUrl = \`/img/products/\${cat.slug}-\${imgIndex}.svg\`;
await prisma.productImage.upsert({
where: { id: \`\${productId}-img-1\` },
update: { url: imageUrl },
create: {
id: \`\${productId}-img-1\`,
productId: product.id,
url: imageUrl,
sortOrder: 0,
},
});
mockImageCount += 1;
}
imageCount += mockImageCount;
// \u2500\u2500 Featured collection`;
content = content.replace(marker, insertion);
fs.writeFileSync(seedPath, content, 'utf-8');
console.log('inserted mock product generator into seed.ts');
-431
View File
@@ -1,431 +0,0 @@
-- CreateEnum
CREATE TYPE "AdminRole" AS ENUM ('SUPER_ADMIN', 'SYSTEM_APPROVER', 'SALES_EXPERT', 'WORKSHOP_SUPERVISOR', 'ACCOUNTANT');
-- CreateEnum
CREATE TYPE "VerificationStep" AS ENUM ('UNVERIFIED', 'STEP_ONE_SUBMITTED', 'STEP_TWO_SUBMITTED', 'VERIFIED');
-- CreateEnum
CREATE TYPE "OrderStatus" AS ENUM ('SUSPENDED', 'PLACED', 'ADMIN_APPROVED', 'SOURCING', 'PREPARING', 'DELIVERED_TO_AGENT', 'DELIVERED_TO_CUSTOMER', 'CANCELLED');
-- CreateEnum
CREATE TYPE "PreOrderStatus" AS ENUM ('PLACED', 'ADMIN_APPROVED', 'WORKSHOP_APPROVED', 'READY_TO_SHIP', 'DELIVERED', 'CANCELLED');
-- CreateEnum
CREATE TYPE "LedgerCurrency" AS ENUM ('GOLD_GRAM', 'RIAL');
-- CreateEnum
CREATE TYPE "LedgerDirection" AS ENUM ('DEBIT', 'CREDIT');
-- CreateEnum
CREATE TYPE "LedgerSourceType" AS ENUM ('ORDER_STOCK', 'ORDER_PRE_ORDER', 'REPAYMENT_MELTED_GOLD', 'REPAYMENT_TRANSFER', 'REPAYMENT_CHEQUE', 'REPAYMENT_CASH', 'WALLET_BONUS', 'ADJUSTMENT');
-- CreateTable
CREATE TABLE "City" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"province" TEXT,
CONSTRAINT "City_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "AdminUser" (
"id" TEXT NOT NULL,
"fullName" TEXT NOT NULL,
"mobile" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"role" "AdminRole" NOT NULL,
"branchId" TEXT,
"wagePercent" DECIMAL(6,3),
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AdminUser_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Customer" (
"id" TEXT NOT NULL,
"fullName" TEXT NOT NULL,
"mobile" TEXT NOT NULL,
"nationalCode" TEXT NOT NULL,
"passwordHash" TEXT NOT NULL,
"cityId" TEXT,
"referredByAgentId" TEXT,
"goldCreditLimitGrams" DECIMAL(14,4) NOT NULL DEFAULT 0,
"goldDebtGrams" DECIMAL(14,4) NOT NULL DEFAULT 0,
"fiatDebtRial" DECIMAL(20,2) NOT NULL DEFAULT 0,
"verificationStep" "VerificationStep" NOT NULL DEFAULT 'UNVERIFIED',
"nationalIdImageUrl" TEXT,
"businessLicenseUrl" TEXT,
"shopImageUrl" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Customer_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CustomerAddress" (
"id" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"cityId" TEXT,
"addressLine" TEXT NOT NULL,
"isDefault" BOOLEAN NOT NULL DEFAULT false,
CONSTRAINT "CustomerAddress_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Branch" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"cityId" TEXT,
CONSTRAINT "Branch_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Workshop" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Workshop_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Category" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"parentId" TEXT,
CONSTRAINT "Category_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Brand" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
CONSTRAINT "Brand_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Collection" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "Collection_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "CollectionProduct" (
"collectionId" TEXT NOT NULL,
"productId" TEXT NOT NULL,
CONSTRAINT "CollectionProduct_pkey" PRIMARY KEY ("collectionId","productId")
);
-- CreateTable
CREATE TABLE "Product" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"categoryId" TEXT,
"brandId" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Product_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProductImage" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"url" TEXT NOT NULL,
"sortOrder" INTEGER NOT NULL DEFAULT 0,
CONSTRAINT "ProductImage_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "ProductVariant" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"title" TEXT NOT NULL,
"sku" TEXT,
"rashWeightGrams" DECIMAL(14,4) NOT NULL,
"defaultWagePercent" DECIMAL(6,3) NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "ProductVariant_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "Order" (
"id" TEXT NOT NULL,
"orderNumber" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"branchId" TEXT,
"status" "OrderStatus" NOT NULL DEFAULT 'PLACED',
"totalGoldWeightGrams" DECIMAL(14,4) NOT NULL,
"wageWeightGrams" DECIMAL(14,4) NOT NULL,
"flatRialCharge" DECIMAL(20,2) NOT NULL DEFAULT 0,
"isSuspendedOverCreditLimit" BOOLEAN NOT NULL DEFAULT false,
"deliveredAt" TIMESTAMP(3),
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Order_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderItem" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"productVariantId" TEXT NOT NULL,
"quantity" INTEGER NOT NULL,
"rashWeightGramsSnapshot" DECIMAL(14,4) NOT NULL,
"wagePercentSnapshot" DECIMAL(6,3) NOT NULL,
"discountPercentSnapshot" DECIMAL(6,3) NOT NULL DEFAULT 0,
"computedWeightGrams" DECIMAL(14,4) NOT NULL,
"flatRialCharge" DECIMAL(20,2) NOT NULL DEFAULT 0,
CONSTRAINT "OrderItem_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "OrderStatusEvent" (
"id" TEXT NOT NULL,
"orderId" TEXT NOT NULL,
"fromStatus" "OrderStatus",
"toStatus" "OrderStatus" NOT NULL,
"note" TEXT,
"actorAdminUserId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "OrderStatusEvent_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PreOrder" (
"id" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"cityId" TEXT,
"workshopId" TEXT,
"status" "PreOrderStatus" NOT NULL DEFAULT 'PLACED',
"designImageUrl" TEXT,
"colorName" TEXT,
"sizeSpec" TEXT,
"stoneColor" TEXT,
"minaColor" TEXT,
"estimatedWeightGrams" DECIMAL(14,4),
"estimatedPriceRial" DECIMAL(20,2),
"description" TEXT,
"convertedOrderId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "PreOrder_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "PreOrderStatusEvent" (
"id" TEXT NOT NULL,
"preOrderId" TEXT NOT NULL,
"changes" JSONB NOT NULL,
"actorAdminUserId" TEXT,
"ipAddress" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PreOrderStatusEvent_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "LedgerEntry" (
"id" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"currency" "LedgerCurrency" NOT NULL,
"direction" "LedgerDirection" NOT NULL,
"sourceType" "LedgerSourceType" NOT NULL,
"amount" DECIMAL(20,4) NOT NULL,
"karatPerMille" INTEGER,
"pricePerGramRialAtEntry" DECIMAL(20,2),
"orderId" TEXT,
"confirmed" BOOLEAN NOT NULL DEFAULT false,
"confirmedAt" TIMESTAMP(3),
"postedByAdminUserId" TEXT,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "LedgerEntry_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "GoldPriceQuote" (
"id" TEXT NOT NULL,
"karatPerMille" INTEGER NOT NULL,
"pricePerGramRial" DECIMAL(20,2) NOT NULL,
"source" TEXT NOT NULL,
"quotedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "GoldPriceQuote_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "WalletBonus" (
"id" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"orderId" TEXT,
"goldGrams" DECIMAL(14,4) NOT NULL,
"grantedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"expiresAt" TIMESTAMP(3) NOT NULL,
"consumedAt" TIMESTAMP(3),
"isActive" BOOLEAN NOT NULL DEFAULT true,
CONSTRAINT "WalletBonus_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "AdminUser_mobile_key" ON "AdminUser"("mobile");
-- CreateIndex
CREATE INDEX "AdminUser_role_idx" ON "AdminUser"("role");
-- CreateIndex
CREATE UNIQUE INDEX "Customer_mobile_key" ON "Customer"("mobile");
-- CreateIndex
CREATE UNIQUE INDEX "Customer_nationalCode_key" ON "Customer"("nationalCode");
-- CreateIndex
CREATE INDEX "Customer_referredByAgentId_idx" ON "Customer"("referredByAgentId");
-- CreateIndex
CREATE UNIQUE INDEX "ProductVariant_sku_key" ON "ProductVariant"("sku");
-- CreateIndex
CREATE UNIQUE INDEX "Order_orderNumber_key" ON "Order"("orderNumber");
-- CreateIndex
CREATE INDEX "Order_customerId_idx" ON "Order"("customerId");
-- CreateIndex
CREATE INDEX "Order_status_idx" ON "Order"("status");
-- CreateIndex
CREATE INDEX "OrderStatusEvent_orderId_idx" ON "OrderStatusEvent"("orderId");
-- CreateIndex
CREATE UNIQUE INDEX "PreOrder_convertedOrderId_key" ON "PreOrder"("convertedOrderId");
-- CreateIndex
CREATE INDEX "PreOrder_customerId_idx" ON "PreOrder"("customerId");
-- CreateIndex
CREATE INDEX "PreOrderStatusEvent_preOrderId_idx" ON "PreOrderStatusEvent"("preOrderId");
-- CreateIndex
CREATE INDEX "LedgerEntry_customerId_currency_idx" ON "LedgerEntry"("customerId", "currency");
-- CreateIndex
CREATE INDEX "LedgerEntry_orderId_idx" ON "LedgerEntry"("orderId");
-- CreateIndex
CREATE INDEX "GoldPriceQuote_karatPerMille_quotedAt_idx" ON "GoldPriceQuote"("karatPerMille", "quotedAt");
-- CreateIndex
CREATE INDEX "WalletBonus_customerId_isActive_idx" ON "WalletBonus"("customerId", "isActive");
-- AddForeignKey
ALTER TABLE "AdminUser" ADD CONSTRAINT "AdminUser_branchId_fkey" FOREIGN KEY ("branchId") REFERENCES "Branch"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Customer" ADD CONSTRAINT "Customer_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Customer" ADD CONSTRAINT "Customer_referredByAgentId_fkey" FOREIGN KEY ("referredByAgentId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CustomerAddress" ADD CONSTRAINT "CustomerAddress_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CustomerAddress" ADD CONSTRAINT "CustomerAddress_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Branch" ADD CONSTRAINT "Branch_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Category" ADD CONSTRAINT "Category_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CollectionProduct" ADD CONSTRAINT "CollectionProduct_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "Collection"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "CollectionProduct" ADD CONSTRAINT "CollectionProduct_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Product" ADD CONSTRAINT "Product_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Product" ADD CONSTRAINT "Product_brandId_fkey" FOREIGN KEY ("brandId") REFERENCES "Brand"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProductImage" ADD CONSTRAINT "ProductImage_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "ProductVariant" ADD CONSTRAINT "ProductVariant_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "Order" ADD CONSTRAINT "Order_branchId_fkey" FOREIGN KEY ("branchId") REFERENCES "Branch"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderItem" ADD CONSTRAINT "OrderItem_productVariantId_fkey" FOREIGN KEY ("productVariantId") REFERENCES "ProductVariant"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderStatusEvent" ADD CONSTRAINT "OrderStatusEvent_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "OrderStatusEvent" ADD CONSTRAINT "OrderStatusEvent_actorAdminUserId_fkey" FOREIGN KEY ("actorAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrder" ADD CONSTRAINT "PreOrder_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrder" ADD CONSTRAINT "PreOrder_cityId_fkey" FOREIGN KEY ("cityId") REFERENCES "City"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrder" ADD CONSTRAINT "PreOrder_workshopId_fkey" FOREIGN KEY ("workshopId") REFERENCES "Workshop"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrder" ADD CONSTRAINT "PreOrder_convertedOrderId_fkey" FOREIGN KEY ("convertedOrderId") REFERENCES "Order"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrderStatusEvent" ADD CONSTRAINT "PreOrderStatusEvent_preOrderId_fkey" FOREIGN KEY ("preOrderId") REFERENCES "PreOrder"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "PreOrderStatusEvent" ADD CONSTRAINT "PreOrderStatusEvent_actorAdminUserId_fkey" FOREIGN KEY ("actorAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LedgerEntry" ADD CONSTRAINT "LedgerEntry_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LedgerEntry" ADD CONSTRAINT "LedgerEntry_orderId_fkey" FOREIGN KEY ("orderId") REFERENCES "Order"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "LedgerEntry" ADD CONSTRAINT "LedgerEntry_postedByAdminUserId_fkey" FOREIGN KEY ("postedByAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "WalletBonus" ADD CONSTRAINT "WalletBonus_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
@@ -0,0 +1,241 @@
-- Sync schema drift (admin/product/customer fields + new tables).
-- NOTE: Do NOT drop "_goldbank_migrations" — that table is owned by the runtime
-- migration runner, not by Prisma schema. Prisma migrate diff wrongly included it.
-- Statements below are idempotent so a partially-applied previous attempt can finish.
-- CreateEnum
DO $$ BEGIN CREATE TYPE "CustomerKind" AS ENUM ('SALES_AGENT', 'APP_SHOPKEEPER', 'WALK_IN_SHOPKEEPER', 'BANKER', 'WORKSHOP_SUPERVISOR'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN CREATE TYPE "OrderChannel" AS ENUM ('RETAIL', 'WHOLESALE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN CREATE TYPE "CommentStatus" AS ENUM ('PENDING', 'APPROVED', 'REJECTED'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN CREATE TYPE "PromotionScope" AS ENUM ('PRODUCT', 'CATEGORY', 'CUSTOMER', 'BRAND'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN CREATE TYPE "PromotionUsage" AS ENUM ('ONCE', 'UNTIL_END_DATE'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN CREATE TYPE "MessageAudience" AS ENUM ('SINGLE_NUMBER', 'MULTIPLE_NUMBERS', 'ALL_CUSTOMERS', 'WHOLESALE_CUSTOMERS', 'RETAIL_CUSTOMERS', 'SALES_AGENTS'); EXCEPTION WHEN duplicate_object THEN NULL; END $$;
-- AlterTable
ALTER TABLE "AdminUser" ADD COLUMN IF NOT EXISTS "userTypeId" INTEGER;
ALTER TABLE "Branch" ADD COLUMN IF NOT EXISTS "address" TEXT;
ALTER TABLE "Branch" ADD COLUMN IF NOT EXISTS "isActive" BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE "Branch" ADD COLUMN IF NOT EXISTS "phone" TEXT;
ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "description" TEXT;
ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "iconUrl" TEXT;
ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "imageUrl" TEXT;
ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "metaTag" TEXT;
ALTER TABLE "Category" ADD COLUMN IF NOT EXISTS "type" INTEGER NOT NULL DEFAULT 1;
ALTER TABLE "City" ADD COLUMN IF NOT EXISTS "percent" DECIMAL(6,3);
ALTER TABLE "City" ADD COLUMN IF NOT EXISTS "percentAux" DECIMAL(6,3);
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "address" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "avatarFileId" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "canPlaceOrders" BOOLEAN NOT NULL DEFAULT true;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "isArchived" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "kind" "CustomerKind" NOT NULL DEFAULT 'APP_SHOPKEEPER';
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "memberCode" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "pasazhName" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "phone" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "storeName" TEXT;
ALTER TABLE "Customer" ADD COLUMN IF NOT EXISTS "zone" TEXT;
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "accountName" TEXT;
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "bankName" TEXT;
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "hallmark" TEXT;
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "labName" TEXT;
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "originalValue" DECIMAL(14,4);
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "priceRial" DECIMAL(20,2);
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "receivedAt" TIMESTAMP(3);
ALTER TABLE "LedgerEntry" ADD COLUMN IF NOT EXISTS "transferTo" TEXT;
ALTER TABLE "Order" ADD COLUMN IF NOT EXISTS "channel" "OrderChannel" NOT NULL DEFAULT 'RETAIL';
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "productCode" TEXT;
ALTER TABLE "Product" ADD COLUMN IF NOT EXISTS "showType" INTEGER NOT NULL DEFAULT 3;
ALTER TABLE "ProductVariant" ADD COLUMN IF NOT EXISTS "isPreOrder" BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE "ProductVariant" ADD COLUMN IF NOT EXISTS "qty" INTEGER NOT NULL DEFAULT 0;
ALTER TABLE "Workshop" ADD COLUMN IF NOT EXISTS "address" TEXT;
ALTER TABLE "Workshop" ADD COLUMN IF NOT EXISTS "phone" TEXT;
ALTER TABLE "Workshop" ADD COLUMN IF NOT EXISTS "supervisorId" TEXT;
-- CreateTable
CREATE TABLE IF NOT EXISTS "user_type" (
"id" INTEGER NOT NULL,
"title" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "user_type_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "permission" (
"rid" TEXT NOT NULL,
"userTypeId" INTEGER NOT NULL,
CONSTRAINT "permission_pkey" PRIMARY KEY ("rid","userTypeId")
);
CREATE TABLE IF NOT EXISTS "Comment" (
"id" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"body" TEXT NOT NULL,
"status" "CommentStatus" NOT NULL DEFAULT 'PENDING',
"replyToId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Comment_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "Setting" (
"key" TEXT NOT NULL,
"value" JSONB NOT NULL,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Setting_pkey" PRIMARY KEY ("key")
);
CREATE TABLE IF NOT EXISTS "Promotion" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"description" TEXT,
"scope" "PromotionScope" NOT NULL DEFAULT 'PRODUCT',
"usage" "PromotionUsage" NOT NULL DEFAULT 'ONCE',
"discountPercent" DECIMAL(6,3) NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"platform" INTEGER NOT NULL DEFAULT 3,
"startsAt" TIMESTAMP(3),
"endsAt" TIMESTAMP(3),
"productId" TEXT,
"categoryId" TEXT,
"customerId" TEXT,
"brandId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
CONSTRAINT "Promotion_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "PromotionExclusion" (
"id" TEXT NOT NULL,
"type" INTEGER NOT NULL,
"itemId" TEXT NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "PromotionExclusion_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "BranchAllocation" (
"id" TEXT NOT NULL,
"branchId" TEXT NOT NULL,
"allocatorAdminUserId" TEXT,
"note" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "BranchAllocation_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "BranchAllocationItem" (
"id" TEXT NOT NULL,
"allocationId" TEXT NOT NULL,
"productId" TEXT NOT NULL,
"weightGrams" DECIMAL(14,4),
CONSTRAINT "BranchAllocationItem_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "SmsMessage" (
"id" TEXT NOT NULL,
"audience" "MessageAudience" NOT NULL,
"recipients" TEXT,
"body" TEXT NOT NULL,
"sentCount" INTEGER NOT NULL DEFAULT 0,
"sentByAdminUserId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "SmsMessage_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "Notification" (
"id" TEXT NOT NULL,
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"audience" "MessageAudience" NOT NULL DEFAULT 'ALL_CUSTOMERS',
"sentByAdminUserId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "Notification_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "NotificationRecipient" (
"id" TEXT NOT NULL,
"notificationId" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"readAt" TIMESTAMP(3),
CONSTRAINT "NotificationRecipient_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "NewsletterSubscriber" (
"id" TEXT NOT NULL,
"email" TEXT,
"mobile" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "NewsletterSubscriber_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "CustomerActivityLog" (
"id" TEXT NOT NULL,
"customerId" TEXT NOT NULL,
"action" TEXT NOT NULL,
"detail" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "CustomerActivityLog_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "UploadedFile" (
"id" TEXT NOT NULL,
"url" TEXT NOT NULL,
"originalName" TEXT NOT NULL,
"mimeType" TEXT NOT NULL,
"sizeBytes" INTEGER NOT NULL,
"purpose" TEXT,
"uploadedByAdminUserId" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "UploadedFile_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE INDEX IF NOT EXISTS "permission_userTypeId_idx" ON "permission"("userTypeId");
CREATE INDEX IF NOT EXISTS "Comment_productId_idx" ON "Comment"("productId");
CREATE INDEX IF NOT EXISTS "Comment_customerId_idx" ON "Comment"("customerId");
CREATE INDEX IF NOT EXISTS "Comment_status_idx" ON "Comment"("status");
CREATE INDEX IF NOT EXISTS "Promotion_scope_isActive_idx" ON "Promotion"("scope", "isActive");
CREATE INDEX IF NOT EXISTS "PromotionExclusion_type_idx" ON "PromotionExclusion"("type");
CREATE UNIQUE INDEX IF NOT EXISTS "PromotionExclusion_type_itemId_key" ON "PromotionExclusion"("type", "itemId");
CREATE INDEX IF NOT EXISTS "BranchAllocation_branchId_idx" ON "BranchAllocation"("branchId");
CREATE UNIQUE INDEX IF NOT EXISTS "BranchAllocationItem_allocationId_productId_key" ON "BranchAllocationItem"("allocationId", "productId");
CREATE INDEX IF NOT EXISTS "SmsMessage_createdAt_idx" ON "SmsMessage"("createdAt");
CREATE INDEX IF NOT EXISTS "Notification_createdAt_idx" ON "Notification"("createdAt");
CREATE INDEX IF NOT EXISTS "NotificationRecipient_customerId_readAt_idx" ON "NotificationRecipient"("customerId", "readAt");
CREATE UNIQUE INDEX IF NOT EXISTS "NotificationRecipient_notificationId_customerId_key" ON "NotificationRecipient"("notificationId", "customerId");
CREATE UNIQUE INDEX IF NOT EXISTS "NewsletterSubscriber_email_key" ON "NewsletterSubscriber"("email");
CREATE UNIQUE INDEX IF NOT EXISTS "NewsletterSubscriber_mobile_key" ON "NewsletterSubscriber"("mobile");
CREATE INDEX IF NOT EXISTS "CustomerActivityLog_customerId_createdAt_idx" ON "CustomerActivityLog"("customerId", "createdAt");
CREATE INDEX IF NOT EXISTS "UploadedFile_purpose_idx" ON "UploadedFile"("purpose");
CREATE INDEX IF NOT EXISTS "AdminUser_userTypeId_idx" ON "AdminUser"("userTypeId");
CREATE UNIQUE INDEX IF NOT EXISTS "Product_productCode_key" ON "Product"("productCode");
-- AddForeignKey
DO $$ BEGIN ALTER TABLE "AdminUser" ADD CONSTRAINT "AdminUser_userTypeId_fkey" FOREIGN KEY ("userTypeId") REFERENCES "user_type"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Customer" ADD CONSTRAINT "Customer_avatarFileId_fkey" FOREIGN KEY ("avatarFileId") REFERENCES "UploadedFile"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Workshop" ADD CONSTRAINT "Workshop_supervisorId_fkey" FOREIGN KEY ("supervisorId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "permission" ADD CONSTRAINT "permission_userTypeId_fkey" FOREIGN KEY ("userTypeId") REFERENCES "user_type"("id") ON DELETE CASCADE ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Comment" ADD CONSTRAINT "Comment_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Comment" ADD CONSTRAINT "Comment_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Comment" ADD CONSTRAINT "Comment_replyToId_fkey" FOREIGN KEY ("replyToId") REFERENCES "Comment"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Promotion" ADD CONSTRAINT "Promotion_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Promotion" ADD CONSTRAINT "Promotion_categoryId_fkey" FOREIGN KEY ("categoryId") REFERENCES "Category"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Promotion" ADD CONSTRAINT "Promotion_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Promotion" ADD CONSTRAINT "Promotion_brandId_fkey" FOREIGN KEY ("brandId") REFERENCES "Brand"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "BranchAllocation" ADD CONSTRAINT "BranchAllocation_branchId_fkey" FOREIGN KEY ("branchId") REFERENCES "Branch"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "BranchAllocation" ADD CONSTRAINT "BranchAllocation_allocatorAdminUserId_fkey" FOREIGN KEY ("allocatorAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "BranchAllocationItem" ADD CONSTRAINT "BranchAllocationItem_allocationId_fkey" FOREIGN KEY ("allocationId") REFERENCES "BranchAllocation"("id") ON DELETE CASCADE ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "BranchAllocationItem" ADD CONSTRAINT "BranchAllocationItem_productId_fkey" FOREIGN KEY ("productId") REFERENCES "Product"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "SmsMessage" ADD CONSTRAINT "SmsMessage_sentByAdminUserId_fkey" FOREIGN KEY ("sentByAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "Notification" ADD CONSTRAINT "Notification_sentByAdminUserId_fkey" FOREIGN KEY ("sentByAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "NotificationRecipient" ADD CONSTRAINT "NotificationRecipient_notificationId_fkey" FOREIGN KEY ("notificationId") REFERENCES "Notification"("id") ON DELETE CASCADE ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "NotificationRecipient" ADD CONSTRAINT "NotificationRecipient_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "CustomerActivityLog" ADD CONSTRAINT "CustomerActivityLog_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "Customer"("id") ON DELETE RESTRICT ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
DO $$ BEGIN ALTER TABLE "UploadedFile" ADD CONSTRAINT "UploadedFile_uploadedByAdminUserId_fkey" FOREIGN KEY ("uploadedByAdminUserId") REFERENCES "AdminUser"("id") ON DELETE SET NULL ON UPDATE CASCADE; EXCEPTION WHEN duplicate_object THEN NULL; END $$;
+6 -6
View File
@@ -346,10 +346,10 @@ async function main() {
prisma.category.upsert({ where: { id: 'cat-coins' }, update: {}, create: { id: 'cat-coins', title: 'سکه و شمش' } }),
]);
const brandHenza = await prisma.brand.upsert({
const brandMontzar = await prisma.brand.upsert({
where: { id: 'brand-henza' },
update: {},
create: { id: 'brand-henza', title: 'هنزا طلا' },
update: { title: 'منتزر طلا' },
create: { id: 'brand-henza', title: 'منتزر طلا' },
});
const brandArtisan = await prisma.brand.upsert({
where: { id: 'brand-artisan' },
@@ -363,7 +363,7 @@ async function main() {
title: 'انگشتر تک نگین کلاسیک',
description: 'انگشتر طلای ۱۸ عیار با طراحی ساده و مینیمال.',
categoryId: ringsCategory.id,
brandId: brandHenza.id,
brandId: brandMontzar.id,
variants: [
{ id: 'variant-ring-size-6', title: 'سایز ۶', sku: 'RING-CLASSIC-6', rashWeightGrams: '2.850', defaultWagePercent: '9.0' },
{ id: 'variant-ring-size-7', title: 'سایز ۷', sku: 'RING-CLASSIC-7', rashWeightGrams: '3.100', defaultWagePercent: '9.0' },
@@ -384,7 +384,7 @@ async function main() {
title: 'گردنبند آویز اشکی',
description: 'گردنبند ظریف با آویز اشکی و زنجیر نازک.',
categoryId: necklacesCategory.id,
brandId: brandHenza.id,
brandId: brandMontzar.id,
variants: [
{ id: 'variant-necklace-40cm', title: 'زنجیر ۴۰ سانتی', sku: 'NCK-TEARDROP-40', rashWeightGrams: '5.600', defaultWagePercent: '10.0' },
{ id: 'variant-necklace-45cm', title: 'زنجیر ۴۵ سانتی', sku: 'NCK-TEARDROP-45', rashWeightGrams: '6.100', defaultWagePercent: '10.0' },
@@ -406,7 +406,7 @@ async function main() {
title: 'سکه تمام بهار آزادی',
description: 'سکه طلای تمام بهار آزادی، عیار استاندارد.',
categoryId: coinsCategory.id,
brandId: brandHenza.id,
brandId: brandMontzar.id,
variants: [
{ id: 'variant-coin-full', title: 'تمام سکه', sku: 'COIN-FULL', rashWeightGrams: '8.133', defaultWagePercent: '2.0' },
{ id: 'variant-coin-half', title: 'نیم سکه', sku: 'COIN-HALF', rashWeightGrams: '4.066', defaultWagePercent: '2.5' },