Let automations fire on more CRM events and open the studio from the main nav.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -32,7 +32,7 @@ import { SmsService } from '../common/sms.service';
|
||||
|
||||
export type AutomationContext = {
|
||||
organizationId: string;
|
||||
entityType: 'deal' | 'ticket';
|
||||
entityType: 'deal' | 'ticket' | 'person' | 'invoice' | 'activity';
|
||||
entityId: string;
|
||||
title: string;
|
||||
personId?: string | null;
|
||||
@@ -182,15 +182,65 @@ export class AutomationsService {
|
||||
ticketPriority: null,
|
||||
};
|
||||
}
|
||||
const ticket = await this.prisma.ticket.findFirst({
|
||||
where: { id: ctx.entityId, organizationId: ctx.organizationId },
|
||||
select: { priority: true, personId: true, companyId: true },
|
||||
});
|
||||
if (ctx.entityType === 'ticket') {
|
||||
const ticket = await this.prisma.ticket.findFirst({
|
||||
where: { id: ctx.entityId, organizationId: ctx.organizationId },
|
||||
select: { priority: true, personId: true, companyId: true },
|
||||
});
|
||||
return {
|
||||
amount: null,
|
||||
hasPerson: Boolean(ticket?.personId ?? ctx.personId),
|
||||
hasCompany: Boolean(ticket?.companyId ?? ctx.companyId),
|
||||
ticketPriority: ticket?.priority ?? null,
|
||||
};
|
||||
}
|
||||
if (ctx.entityType === 'invoice') {
|
||||
const invoice = await this.prisma.invoice.findFirst({
|
||||
where: { id: ctx.entityId, organizationId: ctx.organizationId },
|
||||
select: {
|
||||
personId: true,
|
||||
companyId: true,
|
||||
discountAmount: true,
|
||||
taxPercent: true,
|
||||
lineItems: { select: { quantity: true, unitPrice: true } },
|
||||
},
|
||||
});
|
||||
let amount: number | null = null;
|
||||
if (invoice) {
|
||||
const subtotal = invoice.lineItems.reduce(
|
||||
(sum, item) => sum + Number(item.quantity) * Number(item.unitPrice),
|
||||
0,
|
||||
);
|
||||
const discount = Number(invoice.discountAmount ?? 0);
|
||||
const taxPercent = Number(invoice.taxPercent ?? 0);
|
||||
amount = Math.abs(
|
||||
subtotal - discount + Math.max(0, subtotal - discount) * (taxPercent / 100),
|
||||
);
|
||||
}
|
||||
return {
|
||||
amount,
|
||||
hasPerson: Boolean(invoice?.personId ?? ctx.personId),
|
||||
hasCompany: Boolean(invoice?.companyId ?? ctx.companyId),
|
||||
ticketPriority: null,
|
||||
};
|
||||
}
|
||||
if (ctx.entityType === 'activity') {
|
||||
const activity = await this.prisma.activity.findFirst({
|
||||
where: { id: ctx.entityId, organizationId: ctx.organizationId },
|
||||
select: { personId: true, companyId: true, dealId: true },
|
||||
});
|
||||
return {
|
||||
amount: null,
|
||||
hasPerson: Boolean(activity?.personId ?? ctx.personId),
|
||||
hasCompany: Boolean(activity?.companyId ?? ctx.companyId),
|
||||
ticketPriority: null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
amount: null,
|
||||
hasPerson: Boolean(ticket?.personId ?? ctx.personId),
|
||||
hasCompany: Boolean(ticket?.companyId ?? ctx.companyId),
|
||||
ticketPriority: ticket?.priority ?? null,
|
||||
hasPerson: Boolean(ctx.personId),
|
||||
hasCompany: Boolean(ctx.companyId),
|
||||
ticketPriority: null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -227,10 +277,7 @@ export class AutomationsService {
|
||||
type: 'automation',
|
||||
title: 'فعالیت خودکار',
|
||||
body: title,
|
||||
link:
|
||||
ctx.entityType === 'deal'
|
||||
? `/deals/${ctx.entityId}`
|
||||
: `/tickets/${ctx.entityId}`,
|
||||
link: entityLink(ctx),
|
||||
entityType: ctx.entityType,
|
||||
entityId: ctx.entityId,
|
||||
});
|
||||
@@ -263,10 +310,7 @@ export class AutomationsService {
|
||||
const wantApp = channel === 'app' || channel === 'both';
|
||||
const wantSms = channel === 'sms' || channel === 'both';
|
||||
const smsText = (config.body?.trim() || title).slice(0, 160);
|
||||
const link =
|
||||
ctx.entityType === 'deal'
|
||||
? `/deals/${ctx.entityId}`
|
||||
: `/tickets/${ctx.entityId}`;
|
||||
const link = entityLink(ctx);
|
||||
|
||||
if (config.notifyTarget === 'person') {
|
||||
if (wantSms) {
|
||||
@@ -413,3 +457,12 @@ function defaultTitle(action: AutomationAction, entityTitle: string) {
|
||||
if (action === 'create_notification') return `اتوماسیون: ${entityTitle}`;
|
||||
return entityTitle;
|
||||
}
|
||||
|
||||
function entityLink(ctx: AutomationContext) {
|
||||
if (ctx.entityType === 'deal') return `/deals/${ctx.entityId}`;
|
||||
if (ctx.entityType === 'ticket') return `/tickets/${ctx.entityId}`;
|
||||
if (ctx.entityType === 'invoice') return `/invoices/${ctx.entityId}`;
|
||||
if (ctx.entityType === 'person') return `/contacts/people/${ctx.entityId}`;
|
||||
if (ctx.entityType === 'activity') return `/activities/${ctx.entityId}`;
|
||||
return '/';
|
||||
}
|
||||
|
||||
@@ -12,7 +12,19 @@ import {
|
||||
ValidateIf,
|
||||
} from 'class-validator';
|
||||
|
||||
const TRIGGERS = ['deal_won', 'ticket_resolved'] as const;
|
||||
const TRIGGERS = [
|
||||
'person_created',
|
||||
'lead_submitted',
|
||||
'deal_created',
|
||||
'deal_stage_changed',
|
||||
'deal_won',
|
||||
'deal_lost',
|
||||
'ticket_created',
|
||||
'ticket_resolved',
|
||||
'invoice_issued',
|
||||
'invoice_paid',
|
||||
'activity_done',
|
||||
] as const;
|
||||
const ACTIONS = [
|
||||
'create_activity',
|
||||
'create_notification',
|
||||
|
||||
Reference in New Issue
Block a user