@hed-hog/contact 0.0.325 → 0.0.327
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/person/person.service.d.ts +3 -2
- package/dist/person/person.service.d.ts.map +1 -1
- package/dist/person/person.service.js +28 -3
- package/dist/person/person.service.js.map +1 -1
- package/dist/proposal/proposal.service.d.ts.map +1 -1
- package/dist/proposal/proposal.service.js +14 -0
- package/dist/proposal/proposal.service.js.map +1 -1
- package/hedhog/data/integration_event_catalog.yaml +154 -0
- package/package.json +6 -6
- package/src/person/person.service.ts +31 -2
- package/src/proposal/proposal.service.ts +16 -0
- package/src/person/person.service.spec.ts +0 -143
|
@@ -2,7 +2,7 @@ import { DeleteDTO } from '@hed-hog/api';
|
|
|
2
2
|
import { getLocaleText } from '@hed-hog/api-locale';
|
|
3
3
|
import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
|
|
4
4
|
import { Prisma, PrismaService } from '@hed-hog/api-prisma';
|
|
5
|
-
import { FileService, SettingService } from '@hed-hog/core';
|
|
5
|
+
import { FileService, IntegrationDeveloperApiService, SettingService } from '@hed-hog/core';
|
|
6
6
|
import {
|
|
7
7
|
BadRequestException,
|
|
8
8
|
Inject,
|
|
@@ -300,6 +300,8 @@ export class PersonService {
|
|
|
300
300
|
private readonly fileService: FileService,
|
|
301
301
|
@Inject(forwardRef(() => SettingService))
|
|
302
302
|
private readonly settingService: SettingService,
|
|
303
|
+
@Inject(forwardRef(() => IntegrationDeveloperApiService))
|
|
304
|
+
private readonly integrationApi: IntegrationDeveloperApiService,
|
|
303
305
|
) {}
|
|
304
306
|
|
|
305
307
|
async getStats() {
|
|
@@ -2175,7 +2177,7 @@ export class PersonService {
|
|
|
2175
2177
|
locale,
|
|
2176
2178
|
});
|
|
2177
2179
|
|
|
2178
|
-
|
|
2180
|
+
const result = await this.prismaService.$transaction(async (tx) => {
|
|
2179
2181
|
const person = await tx.person.create({
|
|
2180
2182
|
data: {
|
|
2181
2183
|
name: data.name,
|
|
@@ -2203,6 +2205,16 @@ export class PersonService {
|
|
|
2203
2205
|
|
|
2204
2206
|
return person;
|
|
2205
2207
|
});
|
|
2208
|
+
|
|
2209
|
+
await this.integrationApi.publishEvent({
|
|
2210
|
+
eventName: 'contact.person.created',
|
|
2211
|
+
sourceModule: 'contact',
|
|
2212
|
+
aggregateType: 'person',
|
|
2213
|
+
aggregateId: String(result.id),
|
|
2214
|
+
payload: { id: result.id, name: data.name, type: data.type, status: data.status },
|
|
2215
|
+
}).catch(() => null);
|
|
2216
|
+
|
|
2217
|
+
return result;
|
|
2206
2218
|
}
|
|
2207
2219
|
|
|
2208
2220
|
async update(
|
|
@@ -2299,6 +2311,15 @@ export class PersonService {
|
|
|
2299
2311
|
}
|
|
2300
2312
|
|
|
2301
2313
|
await this.cleanupReplacedAvatar(locale, person.avatar_id, data.avatar_id);
|
|
2314
|
+
|
|
2315
|
+
await this.integrationApi.publishEvent({
|
|
2316
|
+
eventName: 'contact.person.updated',
|
|
2317
|
+
sourceModule: 'contact',
|
|
2318
|
+
aggregateType: 'person',
|
|
2319
|
+
aggregateId: String(id),
|
|
2320
|
+
payload: { id, name: data.name, type: data.type, status: data.status },
|
|
2321
|
+
}).catch(() => null);
|
|
2322
|
+
|
|
2302
2323
|
return result;
|
|
2303
2324
|
});
|
|
2304
2325
|
}
|
|
@@ -2382,6 +2403,14 @@ export class PersonService {
|
|
|
2382
2403
|
},
|
|
2383
2404
|
}),
|
|
2384
2405
|
]);
|
|
2406
|
+
|
|
2407
|
+
await this.integrationApi.publishEvent({
|
|
2408
|
+
eventName: 'contact.person.deleted',
|
|
2409
|
+
sourceModule: 'contact',
|
|
2410
|
+
aggregateType: 'person',
|
|
2411
|
+
aggregateId: ids.join(','),
|
|
2412
|
+
payload: { ids },
|
|
2413
|
+
}).catch(() => null);
|
|
2385
2414
|
}
|
|
2386
2415
|
|
|
2387
2416
|
private async findFollowupSearchPersonIds(
|
|
@@ -537,6 +537,14 @@ export class ProposalService {
|
|
|
537
537
|
current.person_id,
|
|
538
538
|
);
|
|
539
539
|
|
|
540
|
+
await this.integrationApi.publishEvent({
|
|
541
|
+
eventName: 'contact.proposal.updated',
|
|
542
|
+
sourceModule: 'contact',
|
|
543
|
+
aggregateType: 'proposal',
|
|
544
|
+
aggregateId: String(id),
|
|
545
|
+
payload: { id, person_id: current.person_id, title: data.title, status: current.status },
|
|
546
|
+
}).catch(() => null);
|
|
547
|
+
|
|
540
548
|
return this.getById(id, locale);
|
|
541
549
|
}
|
|
542
550
|
|
|
@@ -1452,6 +1460,14 @@ export class ProposalService {
|
|
|
1452
1460
|
),
|
|
1453
1461
|
);
|
|
1454
1462
|
|
|
1463
|
+
await this.integrationApi.publishEvent({
|
|
1464
|
+
eventName: 'contact.proposal.deleted',
|
|
1465
|
+
sourceModule: 'contact',
|
|
1466
|
+
aggregateType: 'proposal',
|
|
1467
|
+
aggregateId: ids.join(','),
|
|
1468
|
+
payload: { ids },
|
|
1469
|
+
}).catch(() => null);
|
|
1470
|
+
|
|
1455
1471
|
return {
|
|
1456
1472
|
deleted: ids.length,
|
|
1457
1473
|
ids,
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
/// <reference types="jest" />
|
|
2
|
-
import { PersonInteractionTypeDTO } from './dto/create-interaction.dto';
|
|
3
|
-
import { PersonService } from './person.service';
|
|
4
|
-
|
|
5
|
-
describe('PersonService CRM interaction/follow-up regression coverage', () => {
|
|
6
|
-
let prisma: any;
|
|
7
|
-
let tx: any;
|
|
8
|
-
let service: PersonService;
|
|
9
|
-
|
|
10
|
-
beforeEach(() => {
|
|
11
|
-
tx = {
|
|
12
|
-
crm_activity: {
|
|
13
|
-
findFirst: jest.fn().mockResolvedValue(null),
|
|
14
|
-
create: jest.fn().mockResolvedValue({ id: 1 }),
|
|
15
|
-
update: jest.fn().mockResolvedValue({ id: 1 }),
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
prisma = {
|
|
20
|
-
$transaction: jest.fn().mockImplementation(async (callback: any) =>
|
|
21
|
-
callback(tx)
|
|
22
|
-
),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
service = new PersonService(
|
|
26
|
-
prisma as any,
|
|
27
|
-
{} as any,
|
|
28
|
-
{} as any,
|
|
29
|
-
{
|
|
30
|
-
getSettingValues: jest.fn().mockResolvedValue({}),
|
|
31
|
-
} as any
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
jest
|
|
35
|
-
.spyOn(service as any, 'ensurePersonAccessible')
|
|
36
|
-
.mockResolvedValue({ id: 42, type: 'individual', status: 'active' });
|
|
37
|
-
jest.spyOn(service as any, 'getPersonOwnerUserId').mockResolvedValue(7);
|
|
38
|
-
jest.spyOn(service as any, 'loadInteractionsFromTx').mockResolvedValue([]);
|
|
39
|
-
jest.spyOn(service as any, 'upsertMetadataValue').mockResolvedValue(undefined);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('persists a completed crm activity when registering an interaction', async () => {
|
|
43
|
-
await service.createInteraction(
|
|
44
|
-
42,
|
|
45
|
-
{
|
|
46
|
-
type: PersonInteractionTypeDTO.CALL,
|
|
47
|
-
notes: 'Ligação inicial',
|
|
48
|
-
},
|
|
49
|
-
'en',
|
|
50
|
-
{ id: 9, name: 'Root User' }
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
expect(tx.crm_activity.create).toHaveBeenCalledWith(
|
|
54
|
-
expect.objectContaining({
|
|
55
|
-
data: expect.objectContaining({
|
|
56
|
-
person_id: 42,
|
|
57
|
-
owner_user_id: 7,
|
|
58
|
-
created_by_user_id: 9,
|
|
59
|
-
completed_by_user_id: 9,
|
|
60
|
-
type: PersonInteractionTypeDTO.CALL,
|
|
61
|
-
subject: 'Call',
|
|
62
|
-
notes: 'Ligação inicial',
|
|
63
|
-
priority: 'medium',
|
|
64
|
-
source_kind: 'interaction',
|
|
65
|
-
due_at: expect.any(Date),
|
|
66
|
-
completed_at: expect.any(Date),
|
|
67
|
-
created_at: expect.any(Date),
|
|
68
|
-
updated_at: expect.any(Date),
|
|
69
|
-
}),
|
|
70
|
-
})
|
|
71
|
-
);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
it('creates an open follow-up activity when none exists', async () => {
|
|
75
|
-
tx.crm_activity.findFirst.mockResolvedValue(null);
|
|
76
|
-
|
|
77
|
-
await service.scheduleFollowup(
|
|
78
|
-
42,
|
|
79
|
-
{
|
|
80
|
-
next_action_at: '2026-04-09T13:48:31.715Z',
|
|
81
|
-
notes: 'Retornar por telefone',
|
|
82
|
-
},
|
|
83
|
-
'en',
|
|
84
|
-
{ id: 9, name: 'Root User' }
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
expect(tx.crm_activity.findFirst).toHaveBeenCalledWith({
|
|
88
|
-
where: {
|
|
89
|
-
person_id: 42,
|
|
90
|
-
source_kind: 'followup',
|
|
91
|
-
completed_at: null,
|
|
92
|
-
},
|
|
93
|
-
orderBy: {
|
|
94
|
-
id: 'desc',
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
expect(tx.crm_activity.create).toHaveBeenCalledWith(
|
|
99
|
-
expect.objectContaining({
|
|
100
|
-
data: expect.objectContaining({
|
|
101
|
-
person_id: 42,
|
|
102
|
-
owner_user_id: 7,
|
|
103
|
-
created_by_user_id: 9,
|
|
104
|
-
type: 'task',
|
|
105
|
-
subject: 'Follow-up',
|
|
106
|
-
notes: 'Retornar por telefone',
|
|
107
|
-
priority: 'medium',
|
|
108
|
-
source_kind: 'followup',
|
|
109
|
-
due_at: new Date('2026-04-09T13:48:31.715Z'),
|
|
110
|
-
}),
|
|
111
|
-
})
|
|
112
|
-
);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('updates the existing open follow-up activity when rescheduling', async () => {
|
|
116
|
-
tx.crm_activity.findFirst.mockResolvedValue({ id: 88 });
|
|
117
|
-
|
|
118
|
-
await service.scheduleFollowup(
|
|
119
|
-
42,
|
|
120
|
-
{
|
|
121
|
-
next_action_at: '2026-04-10T15:00:00.000Z',
|
|
122
|
-
notes: 'Reagendar reunião',
|
|
123
|
-
},
|
|
124
|
-
'en',
|
|
125
|
-
{ id: 11, name: 'Owner User' }
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
expect(tx.crm_activity.update).toHaveBeenCalledWith({
|
|
129
|
-
where: {
|
|
130
|
-
id: 88,
|
|
131
|
-
},
|
|
132
|
-
data: expect.objectContaining({
|
|
133
|
-
owner_user_id: 7,
|
|
134
|
-
type: 'task',
|
|
135
|
-
subject: 'Follow-up',
|
|
136
|
-
notes: 'Reagendar reunião',
|
|
137
|
-
due_at: new Date('2026-04-10T15:00:00.000Z'),
|
|
138
|
-
priority: 'medium',
|
|
139
|
-
updated_at: expect.any(Date),
|
|
140
|
-
}),
|
|
141
|
-
});
|
|
142
|
-
});
|
|
143
|
-
});
|