1419 lines
43 KiB
TypeScript
1419 lines
43 KiB
TypeScript
import { PrismaClient } from '@prisma/client';
|
|
import * as bcrypt from 'bcryptjs';
|
|
import { brand } from '@mento/shared';
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
const DEFAULT_STAGES = [
|
|
{ name: 'سرنخ', isWon: false, isLost: false },
|
|
{ name: 'تماس اول', isWon: false, isLost: false },
|
|
{ name: 'پیشنهاد', isWon: false, isLost: false },
|
|
{ name: 'مذاکره', isWon: false, isLost: false },
|
|
{ name: 'برنده', isWon: true, isLost: false },
|
|
{ name: 'از دسترفته', isWon: false, isLost: true },
|
|
] as const;
|
|
|
|
async function seedPlatformAdmin() {
|
|
const envMobile = (process.env.PLATFORM_ADMIN_MOBILE ?? '').trim();
|
|
const envEmail = (process.env.PLATFORM_ADMIN_EMAIL ?? '').trim().toLowerCase();
|
|
const envPassword = process.env.PLATFORM_ADMIN_PASSWORD ?? '';
|
|
const envName = (process.env.PLATFORM_ADMIN_NAME ?? 'سوپرادمین').trim() || 'سوپرادمین';
|
|
const isProd = process.env.NODE_ENV === 'production';
|
|
const mobileRaw = envMobile || (isProd ? '' : '09120000000');
|
|
const password = envPassword || (isProd ? '' : 'demo1234');
|
|
const email = envEmail || null;
|
|
|
|
if (!mobileRaw || !password) {
|
|
console.log(
|
|
'Platform admin seed skipped (set PLATFORM_ADMIN_MOBILE and PLATFORM_ADMIN_PASSWORD)',
|
|
);
|
|
return;
|
|
}
|
|
if (password.length < 8) {
|
|
console.log('Platform admin seed skipped (password must be at least 8 chars)');
|
|
return;
|
|
}
|
|
|
|
// normalize: 09xxxxxxxxx
|
|
let mobile = mobileRaw.replace(/[\s\-()]/g, '');
|
|
if (mobile.startsWith('+98')) mobile = `0${mobile.slice(3)}`;
|
|
if (mobile.startsWith('98') && mobile.length === 12) mobile = `0${mobile.slice(2)}`;
|
|
if (/^9\d{9}$/.test(mobile)) mobile = `0${mobile}`;
|
|
if (!/^09\d{9}$/.test(mobile)) {
|
|
console.log('Platform admin seed skipped (PLATFORM_ADMIN_MOBILE invalid)');
|
|
return;
|
|
}
|
|
|
|
const passwordHash = await bcrypt.hash(password, 10);
|
|
const existingByMobile = await prisma.platformAdmin.findUnique({ where: { mobile } });
|
|
if (existingByMobile) {
|
|
await prisma.platformAdmin.update({
|
|
where: { id: existingByMobile.id },
|
|
data: {
|
|
passwordHash,
|
|
fullName: envName,
|
|
isActive: true,
|
|
...(email ? { email } : {}),
|
|
},
|
|
});
|
|
console.log('Platform admin password refreshed:', mobile);
|
|
return;
|
|
}
|
|
if (email) {
|
|
const existingByEmail = await prisma.platformAdmin.findUnique({ where: { email } });
|
|
if (existingByEmail) {
|
|
await prisma.platformAdmin.update({
|
|
where: { id: existingByEmail.id },
|
|
data: {
|
|
mobile,
|
|
passwordHash,
|
|
fullName: envName,
|
|
isActive: true,
|
|
},
|
|
});
|
|
console.log('Platform admin linked mobile + password refreshed:', mobile, '→', email);
|
|
return;
|
|
}
|
|
}
|
|
|
|
const orphans = await prisma.platformAdmin.findMany({
|
|
where: { mobile: null },
|
|
take: 2,
|
|
});
|
|
if (orphans.length === 1) {
|
|
const orphan = orphans[0]!;
|
|
await prisma.platformAdmin.update({
|
|
where: { id: orphan.id },
|
|
data: {
|
|
mobile,
|
|
passwordHash,
|
|
fullName: envName,
|
|
isActive: true,
|
|
...(email ? { email } : {}),
|
|
},
|
|
});
|
|
console.log('Platform admin orphan linked mobile:', mobile);
|
|
return;
|
|
}
|
|
|
|
await prisma.platformAdmin.create({
|
|
data: {
|
|
mobile,
|
|
email,
|
|
passwordHash,
|
|
fullName: envName,
|
|
},
|
|
});
|
|
console.log('Seeded platform admin:', mobile);
|
|
}
|
|
|
|
async function main() {
|
|
await seedPlatformAdmin();
|
|
|
|
const email = 'owner@demo.local';
|
|
let org = await prisma.organization.findUnique({ where: { slug: 'demo-mento' } });
|
|
if (!org) {
|
|
org = await prisma.organization.findUnique({ where: { slug: 'demo-ment' } });
|
|
if (org) {
|
|
org = await prisma.organization.update({
|
|
where: { id: org.id },
|
|
data: { slug: 'demo-mento', name: `سازمان نمونه ${brand.nameFa}` },
|
|
});
|
|
console.log('Renamed demo org slug to demo-mento');
|
|
}
|
|
}
|
|
|
|
if (!org) {
|
|
const passwordHash = await bcrypt.hash('demo1234', 10);
|
|
org = await prisma.organization.create({
|
|
data: {
|
|
name: `سازمان نمونه ${brand.nameFa}`,
|
|
slug: 'demo-mento',
|
|
baseCurrency: 'IRR',
|
|
enabledCurrencies: ['IRR', 'USD', 'EUR'],
|
|
businessField: 'tech',
|
|
users: {
|
|
create: {
|
|
email,
|
|
mobile: '09120000001',
|
|
fullName: 'مدیر نمونه',
|
|
passwordHash,
|
|
role: 'owner',
|
|
emailVerifiedAt: new Date(),
|
|
mobileVerifiedAt: new Date(),
|
|
},
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded organization:', org.slug);
|
|
console.log('Login:', email, '/ demo1234');
|
|
} else {
|
|
console.log('Organization exists:', org.slug);
|
|
}
|
|
|
|
const owner = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email },
|
|
});
|
|
if (owner && !owner.mobile) {
|
|
await prisma.user.update({
|
|
where: { id: owner.id },
|
|
data: {
|
|
mobile: '09120000001',
|
|
emailVerifiedAt: owner.emailVerifiedAt ?? new Date(),
|
|
mobileVerifiedAt: owner.mobileVerifiedAt ?? new Date(),
|
|
},
|
|
});
|
|
console.log('Updated demo owner mobile: 09120000001');
|
|
}
|
|
|
|
// چندارزی نمونه
|
|
await prisma.organization.update({
|
|
where: { id: org.id },
|
|
data: {
|
|
baseCurrency: 'IRR',
|
|
enabledCurrencies: ['IRR', 'USD', 'EUR'],
|
|
businessField: 'tech',
|
|
},
|
|
});
|
|
for (const row of [
|
|
{ currency: 'USD', rateToBase: 920_000 },
|
|
{ currency: 'EUR', rateToBase: 1_000_000 },
|
|
] as const) {
|
|
await prisma.exchangeRate.upsert({
|
|
where: {
|
|
organizationId_currency: {
|
|
organizationId: org.id,
|
|
currency: row.currency,
|
|
},
|
|
},
|
|
create: {
|
|
organizationId: org.id,
|
|
currency: row.currency,
|
|
rateToBase: row.rateToBase,
|
|
},
|
|
update: { rateToBase: row.rateToBase },
|
|
});
|
|
}
|
|
|
|
const salesEmail = 'sales@demo.local';
|
|
const existingSales = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: salesEmail },
|
|
});
|
|
if (!existingSales) {
|
|
await prisma.user.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
email: salesEmail,
|
|
mobile: '09120000002',
|
|
fullName: 'کارشناس فروش نمونه',
|
|
passwordHash: await bcrypt.hash('demo1234', 10),
|
|
role: 'sales',
|
|
emailVerifiedAt: new Date(),
|
|
mobileVerifiedAt: new Date(),
|
|
},
|
|
});
|
|
console.log('Seeded sales user:', salesEmail, '/ demo1234');
|
|
}
|
|
|
|
const adminEmail = 'admin@demo.local';
|
|
const existingAdmin = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: adminEmail },
|
|
});
|
|
if (!existingAdmin) {
|
|
await prisma.user.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
email: adminEmail,
|
|
mobile: '09120000003',
|
|
fullName: 'مدیر سازمانی نمونه',
|
|
passwordHash: await bcrypt.hash('demo1234', 10),
|
|
role: 'admin',
|
|
emailVerifiedAt: new Date(),
|
|
mobileVerifiedAt: new Date(),
|
|
},
|
|
});
|
|
console.log('Seeded admin user:', adminEmail, '/ demo1234');
|
|
}
|
|
|
|
async function ensureMembership(
|
|
userId: string,
|
|
organizationId: string,
|
|
role: 'owner' | 'admin' | 'sales' | 'support',
|
|
) {
|
|
await prisma.membership.upsert({
|
|
where: { userId_organizationId: { userId, organizationId } },
|
|
create: { userId, organizationId, role },
|
|
update: { role },
|
|
});
|
|
}
|
|
|
|
const ownerRow = await prisma.user.findFirst({
|
|
where: { email, organizationId: org.id },
|
|
});
|
|
if (ownerRow) await ensureMembership(ownerRow.id, org.id, 'owner');
|
|
const salesRow = await prisma.user.findFirst({ where: { email: salesEmail } });
|
|
if (salesRow) await ensureMembership(salesRow.id, org.id, 'sales');
|
|
const adminRow = await prisma.user.findFirst({ where: { email: adminEmail } });
|
|
if (adminRow) await ensureMembership(adminRow.id, org.id, 'admin');
|
|
|
|
let peopleCount = await prisma.person.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
|
|
if (peopleCount === 0) {
|
|
const acme = await prisma.company.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'شرکت نمونه آکمه',
|
|
phone: '02188776655',
|
|
email: 'info@acme.example',
|
|
address: 'تهران، خیابان ولیعصر',
|
|
notes: 'مشتری بالقوه سازمانی',
|
|
},
|
|
});
|
|
|
|
const nova = await prisma.company.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'نوا تجارت',
|
|
phone: '03132221100',
|
|
email: 'hello@nova.example',
|
|
},
|
|
});
|
|
|
|
await prisma.person.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
companyId: acme.id,
|
|
firstName: 'سارا',
|
|
lastName: 'محمدی',
|
|
email: 'sara@acme.example',
|
|
mobile: '09121234567',
|
|
position: 'مدیر خرید',
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
companyId: acme.id,
|
|
firstName: 'علی',
|
|
lastName: 'رضایی',
|
|
email: 'ali@acme.example',
|
|
mobile: '09129876543',
|
|
position: 'کارشناس فروش',
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
companyId: nova.id,
|
|
firstName: 'مریم',
|
|
lastName: 'کریمی',
|
|
email: 'maryam@nova.example',
|
|
mobile: '09351234567',
|
|
position: 'مدیرعامل',
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
firstName: 'حسین',
|
|
lastName: 'نوری',
|
|
email: 'hosein.nouri@example.com',
|
|
mobile: '09121112233',
|
|
notes: 'سرنخ فردی بدون شرکت',
|
|
},
|
|
],
|
|
});
|
|
console.log('Seeded sample companies and people');
|
|
peopleCount = 4;
|
|
} else {
|
|
console.log('Contacts already seeded');
|
|
}
|
|
|
|
let pipeline = await prisma.pipeline.findFirst({
|
|
where: { organizationId: org.id, isDefault: true },
|
|
include: { stages: { orderBy: { position: 'asc' } } },
|
|
});
|
|
|
|
if (!pipeline) {
|
|
pipeline = await prisma.pipeline.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'مسیر فروش',
|
|
isDefault: true,
|
|
stages: {
|
|
create: DEFAULT_STAGES.map((s, index) => ({
|
|
name: s.name,
|
|
position: index,
|
|
isWon: s.isWon,
|
|
isLost: s.isLost,
|
|
})),
|
|
},
|
|
},
|
|
include: { stages: { orderBy: { position: 'asc' } } },
|
|
});
|
|
console.log('Seeded default pipeline');
|
|
} else {
|
|
console.log('Pipeline already seeded');
|
|
}
|
|
|
|
const servicesPipeline = await prisma.pipeline.findFirst({
|
|
where: { organizationId: org.id, name: 'مسیر خدمات' },
|
|
});
|
|
if (!servicesPipeline) {
|
|
await prisma.pipeline.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'مسیر خدمات',
|
|
isDefault: false,
|
|
stages: {
|
|
create: DEFAULT_STAGES.map((s, index) => ({
|
|
name: s.name,
|
|
position: index,
|
|
isWon: s.isWon,
|
|
isLost: s.isLost,
|
|
})),
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded services pipeline');
|
|
} else {
|
|
console.log('Services pipeline already seeded');
|
|
}
|
|
|
|
// مسیر منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه)
|
|
const WAITING_STAGES = [
|
|
{ name: 'پیگیری', isWon: false, isLost: false },
|
|
{ name: 'مذاکرات اولیه', isWon: false, isLost: false },
|
|
{ name: 'قیمت و نهایی کردن', isWon: false, isLost: false },
|
|
] as const;
|
|
|
|
const WAITING_DEALS: Array<{
|
|
stageName: (typeof WAITING_STAGES)[number]['name'];
|
|
title: string;
|
|
notes: string;
|
|
amount: number;
|
|
expectedCloseAt: string;
|
|
position: number;
|
|
}> = [
|
|
{
|
|
stageName: 'پیگیری',
|
|
title: 'نرمافزار حسابان',
|
|
notes: 'نصب سیستم یکپارچه',
|
|
amount: 250_000_000,
|
|
expectedCloseAt: '2024-07-15T00:00:00.000Z',
|
|
position: 0,
|
|
},
|
|
{
|
|
stageName: 'پیگیری',
|
|
title: 'همکاران سیستم',
|
|
notes: 'نسخه سازمانی',
|
|
amount: 350_000_000,
|
|
expectedCloseAt: '2024-07-08T00:00:00.000Z',
|
|
position: 1,
|
|
},
|
|
{
|
|
stageName: 'پیگیری',
|
|
title: 'بازرگانی امید',
|
|
notes: 'کاربران سازمانی',
|
|
amount: 180_000_000,
|
|
expectedCloseAt: '2024-07-17T00:00:00.000Z',
|
|
position: 2,
|
|
},
|
|
{
|
|
stageName: 'پیگیری',
|
|
title: 'کالای صنعتی پارس',
|
|
notes: 'ماژول انبارداری',
|
|
amount: 120_000_000,
|
|
expectedCloseAt: '2024-07-23T00:00:00.000Z',
|
|
position: 3,
|
|
},
|
|
{
|
|
stageName: 'مذاکرات اولیه',
|
|
title: 'فروشگاه تککالا',
|
|
notes: 'ماژول فروش',
|
|
amount: 280_000_000,
|
|
expectedCloseAt: '2024-07-13T00:00:00.000Z',
|
|
position: 0,
|
|
},
|
|
{
|
|
stageName: 'مذاکرات اولیه',
|
|
title: 'شرکت دادهپرداز',
|
|
notes: 'اتصال به حسابداری',
|
|
amount: 190_000_000,
|
|
expectedCloseAt: '2024-07-10T00:00:00.000Z',
|
|
position: 1,
|
|
},
|
|
{
|
|
stageName: 'مذاکرات اولیه',
|
|
title: 'گروه فنی مهندسی آوا',
|
|
notes: 'ماژول پروژهها',
|
|
amount: 315_000_000,
|
|
expectedCloseAt: '2024-07-05T00:00:00.000Z',
|
|
position: 2,
|
|
},
|
|
{
|
|
stageName: 'مذاکرات اولیه',
|
|
title: 'رستوران زنجیرهای نیک',
|
|
notes: 'ماژول رستوران',
|
|
amount: 165_000_000,
|
|
expectedCloseAt: '2024-07-09T00:00:00.000Z',
|
|
position: 3,
|
|
},
|
|
{
|
|
stageName: 'قیمت و نهایی کردن',
|
|
title: 'شرکت تجارت',
|
|
notes: 'یکپارچهسازی کامل',
|
|
amount: 640_000_000,
|
|
expectedCloseAt: '2024-06-30T00:00:00.000Z',
|
|
position: 0,
|
|
},
|
|
{
|
|
stageName: 'قیمت و نهایی کردن',
|
|
title: 'فروشگاه اینترنتی دیجیخونه',
|
|
notes: 'فروشگاه اینترنتی',
|
|
amount: 420_000_000,
|
|
expectedCloseAt: '2024-06-28T00:00:00.000Z',
|
|
position: 1,
|
|
},
|
|
{
|
|
stageName: 'قیمت و نهایی کردن',
|
|
title: 'شرکت داروسازی بهداشت',
|
|
notes: 'ماژول مالی و انبار',
|
|
amount: 780_000_000,
|
|
expectedCloseAt: '2024-07-02T00:00:00.000Z',
|
|
position: 2,
|
|
},
|
|
];
|
|
|
|
let waitingPipeline = await prisma.pipeline.findFirst({
|
|
where: { organizationId: org.id, name: 'مسیر منتظر' },
|
|
include: { stages: { orderBy: { position: 'asc' } } },
|
|
});
|
|
|
|
if (!waitingPipeline) {
|
|
waitingPipeline = await prisma.pipeline.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'مسیر منتظر',
|
|
isDefault: false,
|
|
stages: {
|
|
create: WAITING_STAGES.map((s, index) => ({
|
|
name: s.name,
|
|
position: index,
|
|
isWon: s.isWon,
|
|
isLost: s.isLost,
|
|
})),
|
|
},
|
|
},
|
|
include: { stages: { orderBy: { position: 'asc' } } },
|
|
});
|
|
console.log('Seeded waiting pipeline (مسیر منتظر)');
|
|
} else {
|
|
console.log('Waiting pipeline already seeded');
|
|
}
|
|
|
|
const waitingDealCount = await prisma.deal.count({
|
|
where: { organizationId: org.id, pipelineId: waitingPipeline.id },
|
|
});
|
|
|
|
if (waitingDealCount === 0) {
|
|
const stageByName = new Map(
|
|
waitingPipeline.stages.map((s) => [s.name, s] as const),
|
|
);
|
|
await prisma.deal.createMany({
|
|
data: WAITING_DEALS.map((d) => {
|
|
const stage = stageByName.get(d.stageName);
|
|
if (!stage) {
|
|
throw new Error(`Missing stage ${d.stageName} on waiting pipeline`);
|
|
}
|
|
return {
|
|
organizationId: org.id,
|
|
pipelineId: waitingPipeline!.id,
|
|
stageId: stage.id,
|
|
title: d.title,
|
|
notes: d.notes,
|
|
amount: d.amount,
|
|
currency: 'IRR',
|
|
expectedCloseAt: new Date(d.expectedCloseAt),
|
|
ownerId: owner?.id,
|
|
position: d.position,
|
|
tags: ['دمو-مسیر'],
|
|
};
|
|
}),
|
|
});
|
|
console.log('Seeded waiting pipeline deals:', WAITING_DEALS.length);
|
|
} else {
|
|
console.log('Waiting pipeline deals already seeded');
|
|
}
|
|
|
|
const dealsCount = await prisma.deal.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
|
|
if (dealsCount === 0) {
|
|
const people = await prisma.person.findMany({
|
|
where: { organizationId: org.id },
|
|
include: { company: true },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const stages = pipeline.stages;
|
|
const lead = stages[0];
|
|
const call = stages[1];
|
|
const proposal = stages[2];
|
|
const negotiation = stages[3];
|
|
|
|
if (!lead || !call || !proposal || !negotiation || people.length === 0) {
|
|
console.log('Skip deals seed: missing stages or people');
|
|
} else {
|
|
await prisma.deal.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
pipelineId: pipeline.id,
|
|
stageId: lead.id,
|
|
title: 'فروش بسته سازمانی آکمه',
|
|
amount: 120_000_000,
|
|
currency: 'IRR',
|
|
source: 'معرفی',
|
|
tags: ['سازمانی', 'اولویتبالا'],
|
|
personId: people[0]?.id,
|
|
companyId: people[0]?.companyId,
|
|
ownerId: owner?.id,
|
|
position: 0,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
pipelineId: pipeline.id,
|
|
stageId: call.id,
|
|
title: 'تمدید قرارداد نوا',
|
|
amount: 45_000_000,
|
|
currency: 'IRR',
|
|
source: 'وبسایت',
|
|
tags: ['تمدید'],
|
|
isStale: true,
|
|
personId: people[2]?.id,
|
|
companyId: people[2]?.companyId,
|
|
ownerId: owner?.id,
|
|
position: 0,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
pipelineId: pipeline.id,
|
|
stageId: proposal.id,
|
|
title: 'مشاوره فردی حسین نوری',
|
|
amount: 8_500_000,
|
|
currency: 'IRR',
|
|
source: 'تماس ورودی',
|
|
tags: ['خرد'],
|
|
personId: people[3]?.id,
|
|
ownerId: owner?.id,
|
|
position: 0,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
pipelineId: pipeline.id,
|
|
stageId: negotiation.id,
|
|
title: 'ارتقای لایسنس آکمه',
|
|
amount: 70_000_000,
|
|
currency: 'IRR',
|
|
tags: ['ارتقاء'],
|
|
isRotten: true,
|
|
personId: people[1]?.id,
|
|
companyId: people[1]?.companyId,
|
|
ownerId: owner?.id,
|
|
position: 0,
|
|
},
|
|
],
|
|
});
|
|
console.log('Seeded sample deals');
|
|
}
|
|
} else {
|
|
console.log('Deals already seeded');
|
|
const sample = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id, title: 'فروش بسته سازمانی آکمه' },
|
|
});
|
|
if (sample && sample.tags.length === 0) {
|
|
await prisma.deal.update({
|
|
where: { id: sample.id },
|
|
data: { tags: ['سازمانی', 'اولویتبالا'] },
|
|
});
|
|
const stale = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id, title: 'تمدید قرارداد نوا' },
|
|
});
|
|
if (stale) {
|
|
await prisma.deal.update({
|
|
where: { id: stale.id },
|
|
data: { tags: ['تمدید'], isStale: true },
|
|
});
|
|
}
|
|
const rotten = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id, title: 'ارتقای لایسنس آکمه' },
|
|
});
|
|
if (rotten) {
|
|
await prisma.deal.update({
|
|
where: { id: rotten.id },
|
|
data: { tags: ['ارتقاء'], isRotten: true },
|
|
});
|
|
}
|
|
console.log('Enriched sample deals with tags/stale/rotten');
|
|
}
|
|
}
|
|
|
|
const activitiesCount = await prisma.activity.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
|
|
if (activitiesCount === 0) {
|
|
const peopleForAct = await prisma.person.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const dealsForAct = await prisma.deal.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
|
|
const now = new Date();
|
|
const todayMorning = new Date(now);
|
|
todayMorning.setHours(10, 0, 0, 0);
|
|
const todayAfternoon = new Date(now);
|
|
todayAfternoon.setHours(15, 30, 0, 0);
|
|
const yesterday = new Date(now);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
yesterday.setHours(11, 0, 0, 0);
|
|
const tomorrow = new Date(now);
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
tomorrow.setHours(12, 0, 0, 0);
|
|
|
|
await prisma.activity.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
type: 'call',
|
|
title: 'تماس پیگیری با سارا محمدی',
|
|
description: 'وضعیت پیشنهاد بسته سازمانی',
|
|
dueAt: todayMorning,
|
|
personId: peopleForAct[0]?.id,
|
|
dealId: dealsForAct[0]?.id,
|
|
assigneeId: owner?.id,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
type: 'meeting',
|
|
title: 'جلسه دمو با نوا تجارت',
|
|
dueAt: todayAfternoon,
|
|
personId: peopleForAct[2]?.id,
|
|
dealId: dealsForAct[1]?.id,
|
|
assigneeId: owner?.id,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
type: 'task',
|
|
title: 'ارسال پیشنهاد قیمت به حسین نوری',
|
|
dueAt: yesterday,
|
|
personId: peopleForAct[3]?.id,
|
|
dealId: dealsForAct[2]?.id,
|
|
assigneeId: owner?.id,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
type: 'task',
|
|
title: 'آمادهسازی قرارداد ارتقا آکمه',
|
|
dueAt: tomorrow,
|
|
personId: peopleForAct[1]?.id,
|
|
dealId: dealsForAct[3]?.id,
|
|
assigneeId: owner?.id,
|
|
},
|
|
],
|
|
});
|
|
console.log('Seeded sample activities');
|
|
} else {
|
|
console.log('Activities already seeded');
|
|
}
|
|
|
|
const notesCount = await prisma.note.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (notesCount === 0) {
|
|
const people = await prisma.person.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const deals = await prisma.deal.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
|
|
const reminder = new Date();
|
|
reminder.setDate(reminder.getDate() + 2);
|
|
reminder.setHours(9, 0, 0, 0);
|
|
|
|
await prisma.note.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
authorId: owner?.id,
|
|
title: 'نکته خریدار آکمه',
|
|
body: 'بودجه امسال تا پایان مهر باید مصرف شود. تصمیمگیر اصلی سارا محمدی است.',
|
|
noteType: 'important',
|
|
pinned: true,
|
|
color: 'mint',
|
|
tags: ['بودجه', 'تصمیمگیر'],
|
|
personId: people[0]?.id,
|
|
dealId: deals[0]?.id,
|
|
reminderAt: reminder,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
authorId: owner?.id,
|
|
body: 'در دمو نوا روی گزارش مسیر فروش خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.',
|
|
noteType: 'call_result',
|
|
color: 'sky',
|
|
tags: ['دمو'],
|
|
personId: people[2]?.id,
|
|
dealId: deals[1]?.id,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
authorId: owner?.id,
|
|
title: 'یادداشت آزاد',
|
|
body: 'حسین نوری ترجیح میدهد تماسها عصر باشد.',
|
|
noteType: 'next_action',
|
|
color: 'sand',
|
|
tags: ['زمانبندی'],
|
|
personId: people[3]?.id,
|
|
},
|
|
],
|
|
});
|
|
console.log('Seeded sample notes');
|
|
} else {
|
|
await prisma.note.updateMany({
|
|
where: { organizationId: org.id, title: 'نکته خریدار آکمه' },
|
|
data: { noteType: 'important' },
|
|
});
|
|
await prisma.note.updateMany({
|
|
where: {
|
|
organizationId: org.id,
|
|
body: {
|
|
contains: 'در دمو نوا',
|
|
},
|
|
},
|
|
data: { noteType: 'call_result' },
|
|
});
|
|
await prisma.note.updateMany({
|
|
where: { organizationId: org.id, title: 'یادداشت آزاد' },
|
|
data: { noteType: 'next_action' },
|
|
});
|
|
console.log('Notes already seeded');
|
|
}
|
|
|
|
const customFieldCount = await prisma.customFieldDefinition.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (customFieldCount === 0) {
|
|
await prisma.customFieldDefinition.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
entityType: 'person',
|
|
key: 'preferred_channel',
|
|
label: 'کانال ترجیحی',
|
|
fieldType: 'select',
|
|
options: ['تلفن', 'واتساپ', 'ایمیل'],
|
|
required: false,
|
|
position: 0,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
entityType: 'company',
|
|
key: 'industry',
|
|
label: 'صنعت',
|
|
fieldType: 'text',
|
|
required: false,
|
|
position: 0,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
entityType: 'deal',
|
|
key: 'priority',
|
|
label: 'اولویت',
|
|
fieldType: 'select',
|
|
options: ['کم', 'متوسط', 'زیاد'],
|
|
required: false,
|
|
position: 0,
|
|
},
|
|
],
|
|
});
|
|
|
|
const person = await prisma.person.findFirst({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
if (person) {
|
|
await prisma.person.update({
|
|
where: { id: person.id },
|
|
data: { customFields: { preferred_channel: 'واتساپ' } },
|
|
});
|
|
}
|
|
console.log('Seeded sample custom fields');
|
|
} else {
|
|
console.log('Custom fields already seeded');
|
|
}
|
|
|
|
const productCount = await prisma.product.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (productCount === 0) {
|
|
const created = await prisma.product.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
name: `لایسنس سازمانی ${brand.nameFa}`,
|
|
sku: 'MENTO-ORG',
|
|
unitPrice: 80_000_000,
|
|
currency: 'IRR',
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
name: 'پشتیبانی سالانه',
|
|
sku: 'MENTO-SUP',
|
|
unitPrice: 20_000_000,
|
|
currency: 'IRR',
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
name: 'مشاوره پیادهسازی',
|
|
sku: 'MENTO-IMP',
|
|
unitPrice: 15_000_000,
|
|
currency: 'IRR',
|
|
},
|
|
],
|
|
});
|
|
void created;
|
|
|
|
const products = await prisma.product.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const deal = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id, title: 'فروش بسته سازمانی آکمه' },
|
|
});
|
|
if (deal && products[0] && products[1]) {
|
|
await prisma.dealLineItem.createMany({
|
|
data: [
|
|
{
|
|
dealId: deal.id,
|
|
productId: products[0].id,
|
|
name: products[0].name,
|
|
quantity: 1,
|
|
unitPrice: products[0].unitPrice ?? 0,
|
|
currency: 'IRR',
|
|
position: 0,
|
|
},
|
|
{
|
|
dealId: deal.id,
|
|
productId: products[1].id,
|
|
name: products[1].name,
|
|
quantity: 1,
|
|
unitPrice: products[1].unitPrice ?? 0,
|
|
currency: 'IRR',
|
|
position: 1,
|
|
},
|
|
],
|
|
});
|
|
await prisma.deal.update({
|
|
where: { id: deal.id },
|
|
data: {
|
|
amount:
|
|
Number(products[0].unitPrice ?? 0) +
|
|
Number(products[1].unitPrice ?? 0),
|
|
},
|
|
});
|
|
}
|
|
console.log('Seeded sample products');
|
|
} else {
|
|
console.log('Products already seeded');
|
|
}
|
|
|
|
if (owner) {
|
|
const sampleDeal = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const sampleActivity = await prisma.activity.findFirst({
|
|
where: { organizationId: org.id, assigneeId: owner.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
|
|
const ensure = async (type: string, data: {
|
|
title: string;
|
|
body: string;
|
|
link: string;
|
|
entityType: string;
|
|
entityId: string | null;
|
|
readAt?: Date | null;
|
|
}) => {
|
|
const found = await prisma.notification.findFirst({
|
|
where: { organizationId: org.id, userId: owner.id, type },
|
|
select: { id: true },
|
|
});
|
|
if (found) return;
|
|
await prisma.notification.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
userId: owner.id,
|
|
type,
|
|
title: data.title,
|
|
body: data.body,
|
|
link: data.link,
|
|
entityType: data.entityType,
|
|
entityId: data.entityId,
|
|
readAt: data.readAt ?? null,
|
|
},
|
|
});
|
|
};
|
|
|
|
await ensure('deal_moved', {
|
|
title: 'جابهجایی معامله در مسیر فروش',
|
|
body: sampleDeal
|
|
? `«${sampleDeal.title}» به مرحله جدید منتقل شد`
|
|
: 'یک معامله جابهجا شد',
|
|
link: sampleDeal ? `/deals/${sampleDeal.id}` : '/pipeline',
|
|
entityType: 'deal',
|
|
entityId: sampleDeal?.id ?? null,
|
|
});
|
|
await ensure('activity_assigned', {
|
|
title: 'فعالیت جدید برای شما',
|
|
body: sampleActivity?.title ?? 'پیگیری نمونه',
|
|
link: sampleActivity
|
|
? `/activities/${sampleActivity.id}`
|
|
: '/activities',
|
|
entityType: 'activity',
|
|
entityId: sampleActivity?.id ?? null,
|
|
});
|
|
await ensure('note_reminder', {
|
|
title: 'یادآوری یادداشت',
|
|
body: 'یادداشت نمونه با یادآور',
|
|
link: '/notes',
|
|
entityType: 'note',
|
|
entityId: null,
|
|
readAt: new Date(),
|
|
});
|
|
console.log('Ensured sample notifications');
|
|
}
|
|
|
|
const leadFormCount = await prisma.leadForm.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (leadFormCount === 0) {
|
|
const defaultPipeline = await prisma.pipeline.findFirst({
|
|
where: { organizationId: org.id, isDefault: true },
|
|
});
|
|
const sales = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: 'sales@demo.local' },
|
|
});
|
|
await prisma.leadForm.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
name: 'فرم تماس سایت',
|
|
source: 'وبسایت',
|
|
pipelineId: defaultPipeline?.id,
|
|
thankYouMessage: 'پیام شما ثبت شد. بهزودی با شما تماس میگیریم.',
|
|
assigneeIds: [owner?.id, sales?.id].filter(Boolean) as string[],
|
|
fields: {
|
|
create: [
|
|
{
|
|
key: 'firstName',
|
|
label: 'نام',
|
|
fieldType: 'text',
|
|
required: true,
|
|
mapTo: 'person.firstName',
|
|
position: 0,
|
|
},
|
|
{
|
|
key: 'lastName',
|
|
label: 'نام خانوادگی',
|
|
fieldType: 'text',
|
|
required: true,
|
|
mapTo: 'person.lastName',
|
|
position: 1,
|
|
},
|
|
{
|
|
key: 'mobile',
|
|
label: 'موبایل',
|
|
fieldType: 'phone',
|
|
required: true,
|
|
mapTo: 'person.mobile',
|
|
position: 2,
|
|
},
|
|
{
|
|
key: 'email',
|
|
label: 'ایمیل',
|
|
fieldType: 'email',
|
|
required: false,
|
|
mapTo: 'person.email',
|
|
position: 3,
|
|
},
|
|
{
|
|
key: 'message',
|
|
label: 'پیام',
|
|
fieldType: 'textarea',
|
|
required: false,
|
|
mapTo: 'deal.notes',
|
|
position: 4,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded sample lead form');
|
|
}
|
|
|
|
const quoteCount = await prisma.quote.count({ where: { organizationId: org.id } });
|
|
if (quoteCount === 0) {
|
|
const deal = await prisma.deal.findFirst({
|
|
where: { organizationId: org.id },
|
|
include: { lineItems: { orderBy: { position: 'asc' } } },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const product = await prisma.product.findFirst({
|
|
where: { organizationId: org.id, isActive: true },
|
|
});
|
|
if (deal) {
|
|
const validUntil = new Date();
|
|
validUntil.setDate(validUntil.getDate() + 14);
|
|
await prisma.quote.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
number: 'PF-0001',
|
|
title: `پیشفاکتور — ${deal.title}`,
|
|
status: 'draft',
|
|
currency: deal.currency || 'IRR',
|
|
notes: 'پیشفاکتور نمونه',
|
|
validUntil,
|
|
personId: deal.personId,
|
|
companyId: deal.companyId,
|
|
dealId: deal.id,
|
|
ownerId: owner?.id ?? deal.ownerId,
|
|
lineItems: {
|
|
create:
|
|
deal.lineItems.length > 0
|
|
? deal.lineItems.map((item, index) => ({
|
|
productId: item.productId,
|
|
name: item.name,
|
|
quantity: item.quantity,
|
|
unitPrice: item.unitPrice,
|
|
currency: item.currency,
|
|
position: index,
|
|
}))
|
|
: [
|
|
{
|
|
productId: product?.id,
|
|
name: product?.name ?? 'خدمت مشاوره',
|
|
quantity: 1,
|
|
unitPrice: product?.unitPrice ?? 15000000,
|
|
currency: 'IRR',
|
|
position: 0,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded sample quote');
|
|
}
|
|
}
|
|
|
|
const invoiceCount = await prisma.invoice.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (invoiceCount === 0) {
|
|
const quote = await prisma.quote.findFirst({
|
|
where: { organizationId: org.id },
|
|
include: { lineItems: { orderBy: { position: 'asc' } } },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const deal = quote
|
|
? null
|
|
: await prisma.deal.findFirst({
|
|
where: { organizationId: org.id },
|
|
include: { lineItems: { orderBy: { position: 'asc' } } },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const source = quote ?? deal;
|
|
if (source) {
|
|
const dueDate = new Date();
|
|
dueDate.setDate(dueDate.getDate() + 30);
|
|
const lineSource = source.lineItems;
|
|
const total = lineSource.reduce(
|
|
(sum, item) => sum + Number(item.quantity) * Number(item.unitPrice),
|
|
0,
|
|
);
|
|
const half = Math.round(total / 2);
|
|
const installment1 = new Date();
|
|
installment1.setDate(installment1.getDate() + 15);
|
|
const installment2 = new Date();
|
|
installment2.setDate(installment2.getDate() + 45);
|
|
|
|
await prisma.invoice.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
number: 'FA-0001',
|
|
title: `فاکتور — ${source.title}`,
|
|
status: 'issued',
|
|
currency: source.currency || 'IRR',
|
|
notes: 'فاکتور نمونه با دو قسط',
|
|
issuedAt: new Date(),
|
|
dueDate,
|
|
personId: source.personId,
|
|
companyId: source.companyId,
|
|
dealId: quote ? quote.dealId : deal!.id,
|
|
quoteId: quote?.id ?? null,
|
|
ownerId: owner?.id ?? null,
|
|
lineItems: {
|
|
create:
|
|
lineSource.length > 0
|
|
? lineSource.map((item, index) => ({
|
|
productId: item.productId,
|
|
name: item.name,
|
|
quantity: item.quantity,
|
|
unitPrice: item.unitPrice,
|
|
currency: item.currency,
|
|
position: index,
|
|
}))
|
|
: [
|
|
{
|
|
name: 'خدمت مشاوره',
|
|
quantity: 1,
|
|
unitPrice: 15000000,
|
|
currency: 'IRR',
|
|
position: 0,
|
|
},
|
|
],
|
|
},
|
|
installments: {
|
|
create: [
|
|
{
|
|
position: 0,
|
|
dueDate: installment1,
|
|
amount: half || 10000000,
|
|
currency: 'IRR',
|
|
status: 'pending',
|
|
},
|
|
{
|
|
position: 1,
|
|
dueDate: installment2,
|
|
amount: total - half || 10000000,
|
|
currency: 'IRR',
|
|
status: 'pending',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded sample invoice');
|
|
}
|
|
}
|
|
|
|
// دسترسی پروندهای: بخشی از پروندهها برای کارشناس فروش
|
|
const salesUser = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: salesEmail },
|
|
});
|
|
if (owner) {
|
|
await prisma.person.updateMany({
|
|
where: { organizationId: org.id, ownerId: null },
|
|
data: { ownerId: owner.id },
|
|
});
|
|
await prisma.company.updateMany({
|
|
where: { organizationId: org.id, ownerId: null },
|
|
data: { ownerId: owner.id },
|
|
});
|
|
}
|
|
if (salesUser && owner) {
|
|
const salesDealCount = await prisma.deal.count({
|
|
where: { organizationId: org.id, ownerId: salesUser.id },
|
|
});
|
|
if (salesDealCount === 0) {
|
|
const dealsForSales = await prisma.deal.findMany({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
take: 2,
|
|
});
|
|
for (const deal of dealsForSales) {
|
|
await prisma.deal.update({
|
|
where: { id: deal.id },
|
|
data: { ownerId: salesUser.id },
|
|
});
|
|
if (deal.personId) {
|
|
await prisma.person.update({
|
|
where: { id: deal.personId },
|
|
data: { ownerId: salesUser.id },
|
|
});
|
|
}
|
|
if (deal.companyId) {
|
|
await prisma.company.update({
|
|
where: { id: deal.companyId },
|
|
data: { ownerId: salesUser.id },
|
|
});
|
|
}
|
|
}
|
|
const act = await prisma.activity.findFirst({
|
|
where: { organizationId: org.id, assigneeId: owner.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
if (act) {
|
|
await prisma.activity.update({
|
|
where: { id: act.id },
|
|
data: { assigneeId: salesUser.id },
|
|
});
|
|
}
|
|
const quote = await prisma.quote.findFirst({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
if (quote) {
|
|
await prisma.quote.update({
|
|
where: { id: quote.id },
|
|
data: { ownerId: salesUser.id },
|
|
});
|
|
}
|
|
console.log('Assigned sample records to sales user for ACL demo');
|
|
}
|
|
}
|
|
|
|
const goalCount = await prisma.salesGoal.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (goalCount === 0 && owner) {
|
|
const now = new Date();
|
|
const periodStart = new Date(
|
|
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), 1),
|
|
);
|
|
const periodEnd = new Date(
|
|
Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 0, 23, 59, 59, 999),
|
|
);
|
|
await prisma.salesGoal.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
userId: null,
|
|
type: 'sales',
|
|
title: 'هدف فروش سازمانی ماه جاری',
|
|
periodStart,
|
|
periodEnd,
|
|
targetAmount: 200_000_000,
|
|
currency: 'IRR',
|
|
notes: 'هدف نمونه برای دمو',
|
|
},
|
|
});
|
|
const salesUser = await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: salesEmail },
|
|
});
|
|
if (salesUser) {
|
|
await prisma.salesGoal.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
userId: salesUser.id,
|
|
type: 'sales',
|
|
title: 'هدف شخصی کارشناس فروش',
|
|
periodStart,
|
|
periodEnd,
|
|
targetAmount: 80_000_000,
|
|
currency: 'IRR',
|
|
},
|
|
});
|
|
}
|
|
console.log('Seeded sample sales goals');
|
|
}
|
|
|
|
const ticketCount = await prisma.ticket.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (ticketCount === 0 && owner) {
|
|
const person = await prisma.person.findFirst({
|
|
where: { organizationId: org.id },
|
|
orderBy: { createdAt: 'asc' },
|
|
});
|
|
const supportOrSales =
|
|
(await prisma.user.findFirst({
|
|
where: { organizationId: org.id, email: 'sales@demo.local' },
|
|
})) ?? owner;
|
|
await prisma.ticket.create({
|
|
data: {
|
|
organizationId: org.id,
|
|
number: 'TK-0001',
|
|
subject: 'درخواست راهنمایی نصب',
|
|
description: 'مشتری برای راهاندازی اولیه کمک میخواهد.',
|
|
status: 'open',
|
|
priority: 'normal',
|
|
personId: person?.id ?? null,
|
|
companyId: person?.companyId ?? null,
|
|
assigneeId: supportOrSales.id,
|
|
reporterId: owner.id,
|
|
slaDueAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
|
messages: {
|
|
create: {
|
|
authorId: owner.id,
|
|
body: 'مشتری برای راهاندازی اولیه کمک میخواهد.',
|
|
isInternal: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
console.log('Seeded sample ticket');
|
|
} else {
|
|
await prisma.ticket.updateMany({
|
|
where: {
|
|
organizationId: org.id,
|
|
slaDueAt: null,
|
|
status: { in: ['open', 'in_progress', 'waiting'] },
|
|
},
|
|
data: {
|
|
slaDueAt: new Date(Date.now() + 24 * 60 * 60 * 1000),
|
|
},
|
|
});
|
|
}
|
|
|
|
const faqCount = await prisma.faqArticle.count({
|
|
where: { organizationId: org.id },
|
|
});
|
|
if (faqCount === 0) {
|
|
await prisma.faqArticle.createMany({
|
|
data: [
|
|
{
|
|
organizationId: org.id,
|
|
question: 'چطور وارد پنل شوم؟',
|
|
answer:
|
|
'از صفحه ورود با ایمیل سازمانی وارد شوید. در صورت فراموشی رمز، از مدیر سازمان بخواهید رمز را بازنشانی کند.',
|
|
category: 'ورود',
|
|
isPublished: true,
|
|
position: 1,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
question: 'نصب اولیه چقدر طول میکشد؟',
|
|
answer:
|
|
'راهاندازی اولیه معمولاً کمتر از یک ساعت است. فایل راهنما و ویدیوی کوتاه در بخش دانش موجود است.',
|
|
category: 'نصب',
|
|
isPublished: true,
|
|
position: 2,
|
|
},
|
|
{
|
|
organizationId: org.id,
|
|
question: 'چطور تیکت پشتیبانی ثبت کنم؟',
|
|
answer:
|
|
'از منوی تیکتها، تیکت جدید بسازید و موضوع و شرح را وارد کنید. قبل از ثبت، پیشنهادهای FAQ را هم ببینید.',
|
|
category: 'پشتیبانی',
|
|
isPublished: true,
|
|
position: 3,
|
|
},
|
|
],
|
|
});
|
|
console.log('Seeded sample FAQ articles');
|
|
}
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|