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:
2026-09-09 21:43:17 +03:30
co-authored by Cursor
parent 51684daa61
commit f1aba1b386
20 changed files with 419 additions and 49 deletions
+2 -1
View File
@@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { ActivitiesController } from './activities.controller';
import { ActivitiesService } from './activities.service';
import { NotificationsModule } from '../notifications/notifications.module';
import { AutomationsModule } from '../automations/automations.module';
@Module({
imports: [NotificationsModule],
imports: [NotificationsModule, AutomationsModule],
controllers: [ActivitiesController],
providers: [ActivitiesService],
exports: [ActivitiesService],
@@ -14,6 +14,7 @@ import type {
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { NotificationsService } from '../notifications/notifications.service';
import { AutomationsService } from '../automations/automations.service';
import { type Actor, assigneeWhere, ownerWhere } from '../common/record-scope';
type ActivityActor = Pick<Actor, 'userId' | 'role'>;
@@ -53,6 +54,7 @@ export class ActivitiesService {
constructor(
private readonly prisma: PrismaService,
private readonly notifications: NotificationsService,
private readonly automations: AutomationsService,
) {}
async summary(
@@ -245,11 +247,18 @@ export class ActivitiesService {
dto.status === 'done' ? new Date() : dto.status === 'open' ? null : undefined;
}
const existing = await this.prisma.activity.findFirst({
where: { id, organizationId },
select: { status: true },
});
const row = await this.prisma.activity.update({
where: { id },
data,
include,
});
if (dto.status === 'done' && existing?.status !== 'done') {
await this.fireDone(organizationId, row);
}
return this.toDto(row);
}
@@ -259,11 +268,18 @@ export class ActivitiesService {
actor: ActivityActor,
): Promise<ActivityDto> {
await this.ensureExists(organizationId, id, actor);
const existing = await this.prisma.activity.findFirst({
where: { id, organizationId },
select: { status: true },
});
const row = await this.prisma.activity.update({
where: { id },
data: { status: 'done', completedAt: new Date() },
include,
});
if (existing?.status !== 'done') {
await this.fireDone(organizationId, row);
}
return this.toDto(row);
}
@@ -350,6 +366,30 @@ export class ActivitiesService {
}
}
private async fireDone(
organizationId: string,
row: {
id: string;
title: string;
person: { id: string } | null;
company: { id: string } | null;
deal: { id: string } | null;
assignee: { id: string } | null;
},
) {
await this.automations.runTrigger('activity_done', {
organizationId,
entityType: 'activity',
entityId: row.id,
title: row.title,
personId: row.person?.id ?? null,
companyId: row.company?.id ?? null,
dealId: row.deal?.id ?? null,
assigneeId: row.assignee?.id ?? null,
ownerId: row.assignee?.id ?? null,
});
}
private async ensureExists(
organizationId: string,
id: string,