rename: کاریز فروش → مسیر فروش, کاریز پروژه → تابلوی پروژه

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-06 20:51:50 +03:30
co-authored by Cursor
parent b3784818fc
commit b4ffef9b6f
29 changed files with 131 additions and 120 deletions
@@ -0,0 +1,2 @@
-- Rename the default sales pipeline: «کاریز فروش» → «مسیر فروش»
UPDATE "Pipeline" SET "name" = 'مسیر فروش' WHERE "name" = 'کاریز فروش';
+2 -2
View File
@@ -1231,7 +1231,7 @@ model PortalOtp {
@@index([expiresAt])
}
/// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از کاریز فروش)
/// برد کانبان برای کارهای داخلی / پس‌ازفروش (جدا از مسیر فروش)
model ProjectBoard {
id String @id @default(cuid())
organizationId String
@@ -1775,7 +1775,7 @@ model AuditLog {
@@index([organizationId, entityType, createdAt])
}
/// فیلترهای ذخیره‌شده برای لیست‌ها و کاریزها
/// فیلترهای ذخیره‌شده برای لیست‌ها و مسیرهای فروش
/// ستون‌های مخفی هر جدول برای هر کاربر (به‌ازای هر سازمان)
model UserTablePreference {
id String @id @default(cuid())
+10 -10
View File
@@ -335,7 +335,7 @@ async function main() {
pipeline = await prisma.pipeline.create({
data: {
organizationId: org.id,
name: 'کاریز فروش',
name: 'مسیر فروش',
isDefault: true,
stages: {
create: DEFAULT_STAGES.map((s, index) => ({
@@ -354,13 +354,13 @@ async function main() {
}
const servicesPipeline = await prisma.pipeline.findFirst({
where: { organizationId: org.id, name: 'کاریز خدمات' },
where: { organizationId: org.id, name: 'مسیر خدمات' },
});
if (!servicesPipeline) {
await prisma.pipeline.create({
data: {
organizationId: org.id,
name: 'کاریز خدمات',
name: 'مسیر خدمات',
isDefault: false,
stages: {
create: DEFAULT_STAGES.map((s, index) => ({
@@ -377,7 +377,7 @@ async function main() {
console.log('Services pipeline already seeded');
}
// کاریز منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه)
// مسیر منتظر — مطابق طراحی UI (۳ مرحله + معاملات نمونه)
const WAITING_STAGES = [
{ name: 'پیگیری', isWon: false, isLost: false },
{ name: 'مذاکرات اولیه', isWon: false, isLost: false },
@@ -483,7 +483,7 @@ async function main() {
];
let waitingPipeline = await prisma.pipeline.findFirst({
where: { organizationId: org.id, name: 'کاریز منتظر' },
where: { organizationId: org.id, name: 'مسیر منتظر' },
include: { stages: { orderBy: { position: 'asc' } } },
});
@@ -491,7 +491,7 @@ async function main() {
waitingPipeline = await prisma.pipeline.create({
data: {
organizationId: org.id,
name: 'کاریز منتظر',
name: 'مسیر منتظر',
isDefault: false,
stages: {
create: WAITING_STAGES.map((s, index) => ({
@@ -504,7 +504,7 @@ async function main() {
},
include: { stages: { orderBy: { position: 'asc' } } },
});
console.log('Seeded waiting pipeline (کاریز منتظر)');
console.log('Seeded waiting pipeline (مسیر منتظر)');
} else {
console.log('Waiting pipeline already seeded');
}
@@ -534,7 +534,7 @@ async function main() {
expectedCloseAt: new Date(d.expectedCloseAt),
ownerId: owner?.id,
position: d.position,
tags: ['دمو-کاریز'],
tags: ['دمو-مسیر'],
};
}),
});
@@ -763,7 +763,7 @@ async function main() {
{
organizationId: org.id,
authorId: owner?.id,
body: 'در دمو نوا روی گزارش کاریز خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.',
body: 'در دمو نوا روی گزارش مسیر فروش خیلی حساس بودند. نسخه چاپی PDF را آماده کنیم.',
noteType: 'call_result',
color: 'sky',
tags: ['دمو'],
@@ -971,7 +971,7 @@ async function main() {
};
await ensure('deal_moved', {
title: 'جابه‌جایی معامله در کاریز',
title: 'جابه‌جایی معامله در مسیر فروش',
body: sampleDeal
? `«${sampleDeal.title}» به مرحله جدید منتقل شد`
: 'یک معامله جابه‌جا شد',
@@ -677,7 +677,7 @@ export class LeadFormsService {
}));
if (!pipeline?.stages.length) {
throw new BadRequestException('کاریز فعالی برای ثبت معامله وجود ندارد');
throw new BadRequestException('مسیر فروش فعالی برای ثبت معامله وجود ندارد');
}
const stage =
@@ -744,7 +744,7 @@ export class LeadFormsService {
where: { id, organizationId },
select: { id: true },
});
if (!found) throw new BadRequestException('کاریز انتخاب‌شده معتبر نیست');
if (!found) throw new BadRequestException('مسیر فروش انتخاب‌شده معتبر نیست');
}
private async assertLeadStage(
@@ -502,7 +502,7 @@ export class DealsExcelService {
}
/**
* مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از کاریزهای واردشده حذف می‌شوند
* مراحل پیش‌فرض سیستم (سرنخ، تماس اول، …) که خالی مانده‌اند از مسیرهای فروش واردشده حذف می‌شوند
* تا فقط مراحل خود فایل دیده شوند.
*/
private async removeUnusedDefaultStages(organizationId: string, pipelineIds: Set<string>) {
@@ -634,7 +634,8 @@ export class DealsExcelService {
status: DealStatus,
ctx: RowContext,
) {
const name = pipelineName?.trim() || 'کاریز فروش';
const rawName = pipelineName?.trim();
const name = !rawName || rawName === 'کاریز فروش' ? 'مسیر فروش' : rawName;
const cacheKey = name.toLowerCase();
let pipeline = ctx.pipelines.get(cacheKey);
if (!pipeline) {
@@ -677,7 +678,7 @@ export class DealsExcelService {
}
const added = await this.pipelines.createStage(organizationId, pipeline.id, wanted);
const stage = added.stages.find((s) => s.name === wanted) ?? added.stages.at(-1);
if (!stage) throw new Error('کاریز مرحله‌ای ندارد');
if (!stage) throw new Error('مسیر فروش مرحله‌ای ندارد');
pipeline.stages = added.stages.map((s) => ({
id: s.id,
name: s.name,
@@ -688,7 +689,7 @@ export class DealsExcelService {
}
const stage = byStatusExisting ?? pipeline.stages[0];
if (!stage) throw new Error('کاریز مرحله‌ای ندارد');
if (!stage) throw new Error('مسیر فروش مرحله‌ای ندارد');
return { pipelineId: pipeline.id, stageId: stage.id, pipelineName: pipeline.name };
}
+3 -3
View File
@@ -92,7 +92,7 @@ export class DealsService {
resolvedPipeline.stages[0];
if (!stage) {
throw new BadRequestException('کاریز مرحله‌ای ندارد');
throw new BadRequestException('مسیر فروش مرحله‌ای ندارد');
}
await this.assertLinks(organizationId, actor, dto.personId, dto.companyId);
@@ -337,7 +337,7 @@ export class DealsService {
where: { id: dto.stageId, pipelineId: deal.pipelineId },
});
if (!stage) {
throw new BadRequestException('مرحله در این کاریز معتبر نیست');
throw new BadRequestException('مرحله در این مسیر فروش معتبر نیست');
}
const status = stage.isWon ? 'won' : stage.isLost ? 'lost' : 'open';
@@ -369,7 +369,7 @@ export class DealsService {
organizationId,
userId: deal.ownerId,
type: 'deal_moved',
title: 'جابه‌جایی معامله در کاریز',
title: 'جابه‌جایی معامله در مسیر فروش',
body: `«${deal.title}» به مرحله «${stage.name}» منتقل شد`,
link: `/deals/${deal.id}`,
entityType: 'deal',
+2 -2
View File
@@ -81,7 +81,7 @@ export class PipelinesService {
const created = await this.prisma.pipeline.create({
data: {
organizationId,
name: 'کاریز فروش',
name: 'مسیر فروش',
isDefault: true,
stages: {
create: DEFAULT_STAGES.map((s, index) => ({
@@ -389,7 +389,7 @@ export class PipelinesService {
where: { id: pipelineId, organizationId },
include: { stages: { orderBy: { position: 'asc' } } },
});
if (!pipeline) throw new NotFoundException('کاریز پیدا نشد');
if (!pipeline) throw new NotFoundException('مسیر فروش پیدا نشد');
return pipeline;
}
@@ -33,8 +33,8 @@ const CASE_COLUMNS = [
{ header: 'نام خانوادگی شخص کارت', key: 'personName', width: 22 },
{ header: 'کد دیدار شخص', key: 'personCode', width: 14 },
{ header: 'موبایل شخص کارت', key: 'personMobile', width: 16 },
{ header: 'کاریز کارت', key: 'boardName', width: 20 },
{ header: 'مرحله کاریز کارت', key: 'stageName', width: 18 },
{ header: 'تابلوی پروژه کارت', key: 'boardName', width: 20 },
{ header: 'مرحله تابلوی پروژه کارت', key: 'stageName', width: 18 },
{ header: 'تاریخ انجام کارت', key: 'doneDate', width: 16 },
{ header: 'برچسب', key: 'tag', width: 16 },
];
@@ -53,6 +53,9 @@ const CASE_ALIASES: Record<string, string> = {
نامخانوادگیشخصکارت: 'personName',
کددیدارشخص: 'personCode',
موبایلشخصکارت: 'personMobile',
تابلویپروژهکارت: 'boardName',
مرحلهتابلویپروژهکارت: 'stageName',
// سرستون‌های قدیمی (قبل از تغییر نام کاریز → تابلوی پروژه)
کاریزکارت: 'boardName',
مرحلهکاریزکارت: 'stageName',
تاریخانجامکارت: 'doneDate',