@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.
@@ -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
- return this.prismaService.$transaction(async (tx) => {
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
- });