Files
crm/apps/api/prisma/seed.ts
T
MahdiandCursor d2f66d11fc Ask for organization business field at signup, including jewelry.
Store Didar-style industry on the org so registration, settings, and Ask Mento share the same catalog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-03 16:45:59 +03:30

1113 lines
34 KiB
TypeScript

import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcryptjs';
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 main() {
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: 'سازمان نمونه منتو' },
});
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: 'سازمان نمونه منتو',
slug: 'demo-mento',
baseCurrency: 'IRR',
enabledCurrencies: ['IRR', 'USD', 'EUR'],
businessField: 'tech',
users: {
create: {
email,
fullName: 'مدیر نمونه',
passwordHash,
role: 'owner',
},
},
},
});
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 },
});
// چندارزی نمونه
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,
fullName: 'کارشناس فروش نمونه',
passwordHash: await bcrypt.hash('demo1234', 10),
role: 'sales',
},
});
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,
fullName: 'مدیر سازمانی نمونه',
passwordHash: await bcrypt.hash('demo1234', 10),
role: 'admin',
},
});
console.log('Seeded admin user:', adminEmail, '/ demo1234');
}
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');
}
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: 'لایسنس سازمانی منتو',
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,
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,
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();
});