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
View File
@@ -1,8 +1,10 @@
import { Module } from '@nestjs/common';
import { AutomationsModule } from '../automations/automations.module';
import { InvoicesController } from './invoices.controller';
import { InvoicesService } from './invoices.service';
@Module({
imports: [AutomationsModule],
controllers: [InvoicesController],
providers: [InvoicesService],
exports: [InvoicesService],
+47 -4
View File
@@ -16,6 +16,7 @@ import type {
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { type Actor, ownerWhere } from '../common/record-scope';
import { AutomationsService } from '../automations/automations.service';
type InvoiceActor = Pick<Actor, 'userId' | 'role'>;
@@ -37,7 +38,10 @@ type InvoiceRow = Prisma.InvoiceGetPayload<{ include: typeof include }>;
@Injectable()
export class InvoicesService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly automations: AutomationsService,
) {}
async list(
organizationId: string,
@@ -264,7 +268,11 @@ export class InvoicesService {
},
include,
});
return this.toDto(row);
const created = this.toDto(row);
if (created.status !== 'draft' && created.status !== 'canceled') {
await this.fireIssued(organizationId, created);
}
return created;
}
async update(
@@ -374,7 +382,15 @@ export class InvoicesService {
await this.recomputeStatus(id);
}
return this.get(organizationId, id, actor);
const row = await this.get(organizationId, id, actor);
const leftDraft =
existing.status === 'draft' &&
row.status !== 'draft' &&
row.status !== 'canceled';
if (leftDraft) {
await this.fireIssued(organizationId, row);
}
return row;
}
async remove(
@@ -443,7 +459,21 @@ export class InvoicesService {
});
await this.recomputeStatus(invoiceId);
return this.get(organizationId, invoiceId, actor);
const row = await this.get(organizationId, invoiceId, actor);
if (invoice.status === 'draft') {
await this.fireIssued(organizationId, row);
}
await this.automations.runTrigger('invoice_paid', {
organizationId,
entityType: 'invoice',
entityId: row.id,
title: row.title || row.number,
personId: row.person?.id ?? null,
companyId: row.company?.id ?? null,
dealId: row.deal?.id ?? null,
ownerId: row.owner?.id ?? null,
});
return row;
}
async removePayment(
@@ -549,6 +579,19 @@ export class InvoicesService {
return `FA-${String(count + 1).padStart(4, '0')}`;
}
private async fireIssued(organizationId: string, row: InvoiceDto) {
await this.automations.runTrigger('invoice_issued', {
organizationId,
entityType: 'invoice',
entityId: row.id,
title: row.title || row.number,
personId: row.person?.id ?? null,
companyId: row.company?.id ?? null,
dealId: row.deal?.id ?? null,
ownerId: row.owner?.id ?? null,
});
}
private async ensureExists(
organizationId: string,
id: string,