@hed-hog/contact 0.0.301 → 0.0.302

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.
Files changed (36) hide show
  1. package/dist/person/person.service.d.ts +2 -0
  2. package/dist/person/person.service.d.ts.map +1 -1
  3. package/dist/person/person.service.js +111 -127
  4. package/dist/person/person.service.js.map +1 -1
  5. package/dist/person/person.service.spec.d.ts +2 -0
  6. package/dist/person/person.service.spec.d.ts.map +1 -0
  7. package/dist/person/person.service.spec.js +106 -0
  8. package/dist/person/person.service.spec.js.map +1 -0
  9. package/dist/proposal/proposal.service.d.ts +5 -0
  10. package/dist/proposal/proposal.service.d.ts.map +1 -1
  11. package/dist/proposal/proposal.service.js +242 -19
  12. package/dist/proposal/proposal.service.js.map +1 -1
  13. package/dist/proposal/proposal.service.spec.js +153 -165
  14. package/dist/proposal/proposal.service.spec.js.map +1 -1
  15. package/hedhog/data/menu.yaml +35 -18
  16. package/hedhog/frontend/app/accounts/_components/account-form-sheet.tsx.ejs +517 -346
  17. package/hedhog/frontend/app/activities/_components/activity-detail-sheet.tsx.ejs +42 -17
  18. package/hedhog/frontend/app/activities/_components/activity-types.ts.ejs +1 -1
  19. package/hedhog/frontend/app/activities/page.tsx.ejs +315 -101
  20. package/hedhog/frontend/app/follow-ups/page.tsx.ejs +172 -22
  21. package/hedhog/frontend/app/page.tsx.ejs +1 -1
  22. package/hedhog/frontend/app/person/_components/person-form-sheet.tsx.ejs +1 -1
  23. package/hedhog/frontend/app/pipeline/_components/lead-detail-sheet.tsx.ejs +1 -1
  24. package/hedhog/frontend/app/pipeline/_components/lead-proposals-tab.tsx.ejs +505 -428
  25. package/hedhog/frontend/app/pipeline/page.tsx.ejs +30 -4
  26. package/hedhog/frontend/app/proposals/_components/proposals-management-page.tsx.ejs +773 -0
  27. package/hedhog/frontend/app/proposals/approvals/page.tsx.ejs +5 -0
  28. package/hedhog/frontend/app/proposals/page.tsx.ejs +5 -0
  29. package/hedhog/frontend/app/reports/page.tsx.ejs +431 -375
  30. package/hedhog/frontend/messages/en.json +100 -1
  31. package/hedhog/frontend/messages/pt.json +100 -1
  32. package/package.json +6 -6
  33. package/src/person/person.service.spec.ts +143 -0
  34. package/src/person/person.service.ts +147 -158
  35. package/src/proposal/proposal.service.spec.ts +196 -0
  36. package/src/proposal/proposal.service.ts +348 -18
@@ -4,39 +4,40 @@ import { PaginationDTO, PaginationService } from '@hed-hog/api-pagination';
4
4
  import { Prisma, PrismaService } from '@hed-hog/api-prisma';
5
5
  import { FileService, SettingService } from '@hed-hog/core';
6
6
  import {
7
- BadRequestException,
8
- Inject,
9
- Injectable,
10
- NotFoundException,
11
- forwardRef,
7
+ BadRequestException,
8
+ Inject,
9
+ Injectable,
10
+ Logger,
11
+ NotFoundException,
12
+ forwardRef,
12
13
  } from '@nestjs/common';
13
14
  import {
14
- ACCOUNT_LIFECYCLE_STAGES,
15
- type AccountLifecycleStage,
16
- type CreateAccountDTO,
17
- type UpdateAccountDTO,
15
+ ACCOUNT_LIFECYCLE_STAGES,
16
+ type AccountLifecycleStage,
17
+ type CreateAccountDTO,
18
+ type UpdateAccountDTO,
18
19
  } from './dto/account.dto';
19
20
  import {
20
- type ActivityListQueryDTO,
21
- type CrmActivityPriority,
22
- type CrmActivitySourceKind,
23
- type CrmActivityStatus,
24
- type CrmActivityType,
21
+ type ActivityListQueryDTO,
22
+ type CrmActivityPriority,
23
+ type CrmActivitySourceKind,
24
+ type CrmActivityStatus,
25
+ type CrmActivityType,
25
26
  } from './dto/activity.dto';
26
27
  import { CreateFollowupDTO } from './dto/create-followup.dto';
27
28
  import {
28
- CreateInteractionDTO,
29
- PersonInteractionTypeDTO,
29
+ CreateInteractionDTO,
30
+ PersonInteractionTypeDTO,
30
31
  } from './dto/create-interaction.dto';
31
32
  import { CreateDTO } from './dto/create.dto';
32
33
  import {
33
- type CrmDashboardPeriod,
34
- type DashboardQueryDTO,
34
+ type CrmDashboardPeriod,
35
+ type DashboardQueryDTO,
35
36
  } from './dto/dashboard-query.dto';
36
37
  import { CheckPersonDuplicatesQueryDTO } from './dto/duplicates-query.dto';
37
38
  import {
38
- FollowupListQueryDTO,
39
- FollowupStatsQueryDTO,
39
+ FollowupListQueryDTO,
40
+ FollowupStatsQueryDTO,
40
41
  } from './dto/followup-query.dto';
41
42
  import { MergePersonDTO } from './dto/merge.dto';
42
43
  import { ReportsQueryDTO, type CrmReportGroupBy } from './dto/reports-query.dto';
@@ -286,6 +287,8 @@ type CrmReportPayload = {
286
287
 
287
288
  @Injectable()
288
289
  export class PersonService {
290
+ private readonly logger = new Logger(PersonService.name);
291
+
289
292
  constructor(
290
293
  @Inject(forwardRef(() => PrismaService))
291
294
  private readonly prismaService: PrismaService,
@@ -2055,17 +2058,17 @@ export class PersonService {
2055
2058
  LIFECYCLE_STAGE_METADATA_KEY,
2056
2059
  data.lifecycle_stage,
2057
2060
  );
2058
-
2059
- if (nextLifecycleStage && currentLifecycleStage !== nextLifecycleStage) {
2060
- await this.registerStageTransition(tx, {
2061
- personId: person.id,
2062
- fromStage: currentLifecycleStage,
2063
- toStage: nextLifecycleStage,
2064
- changedByUserId: actorUserId,
2065
- });
2066
- }
2067
2061
  });
2068
2062
 
2063
+ if (nextLifecycleStage && currentLifecycleStage !== nextLifecycleStage) {
2064
+ await this.registerStageTransitionSafely(this.prismaService, {
2065
+ personId: person.id,
2066
+ fromStage: currentLifecycleStage,
2067
+ toStage: nextLifecycleStage,
2068
+ changedByUserId: actorUserId,
2069
+ });
2070
+ }
2071
+
2069
2072
  return {
2070
2073
  success: true,
2071
2074
  lifecycle_stage: data.lifecycle_stage,
@@ -2137,6 +2140,15 @@ export class PersonService {
2137
2140
 
2138
2141
  const currentLifecycleStage = await this.getPersonLifecycleStage(id);
2139
2142
  const nextLifecycleStage = this.normalizeTextOrNull(data.lifecycle_stage);
2143
+ const stageTransitionParams =
2144
+ nextLifecycleStage && currentLifecycleStage !== nextLifecycleStage
2145
+ ? {
2146
+ personId: id,
2147
+ fromStage: currentLifecycleStage,
2148
+ toStage: nextLifecycleStage,
2149
+ changedByUserId: this.coerceNumber(user?.id) || null,
2150
+ }
2151
+ : null;
2140
2152
 
2141
2153
  const incomingContacts = Array.isArray(data.contacts) ? data.contacts : [];
2142
2154
  const incomingAddresses = Array.isArray(data.addresses) ? data.addresses : [];
@@ -2182,18 +2194,16 @@ export class PersonService {
2182
2194
  await this.syncAddresses(tx, id, incomingAddresses, locale);
2183
2195
  await this.syncDocuments(tx, id, incomingDocuments);
2184
2196
 
2185
- if (nextLifecycleStage && currentLifecycleStage !== nextLifecycleStage) {
2186
- await this.registerStageTransition(tx, {
2187
- personId: id,
2188
- fromStage: currentLifecycleStage,
2189
- toStage: nextLifecycleStage,
2190
- changedByUserId: this.coerceNumber(user?.id) || null,
2191
- });
2192
- }
2193
-
2194
2197
  return { success: true };
2195
2198
  })
2196
2199
  .then(async (result) => {
2200
+ if (stageTransitionParams) {
2201
+ await this.registerStageTransitionSafely(
2202
+ this.prismaService,
2203
+ stageTransitionParams,
2204
+ );
2205
+ }
2206
+
2197
2207
  await this.cleanupReplacedAvatar(locale, person.avatar_id, data.avatar_id);
2198
2208
  return result;
2199
2209
  });
@@ -3068,8 +3078,28 @@ export class PersonService {
3068
3078
  return this.metadataToString(metadata?.value) ?? 'new';
3069
3079
  }
3070
3080
 
3081
+ private async registerStageTransitionSafely(
3082
+ client: any,
3083
+ params: {
3084
+ personId: number;
3085
+ fromStage: string | null;
3086
+ toStage: string;
3087
+ changedByUserId: number | null;
3088
+ },
3089
+ ) {
3090
+ try {
3091
+ await this.registerStageTransition(client, params);
3092
+ } catch (error) {
3093
+ const reason = error instanceof Error ? error.message : String(error);
3094
+
3095
+ this.logger.warn(
3096
+ `Failed to persist CRM stage history for person ${params.personId} (${params.fromStage ?? 'null'} -> ${params.toStage}). Continuing without audit entry. Reason: ${reason}`,
3097
+ );
3098
+ }
3099
+ }
3100
+
3071
3101
  private async registerStageTransition(
3072
- tx: any,
3102
+ client: any,
3073
3103
  {
3074
3104
  personId,
3075
3105
  fromStage,
@@ -3086,32 +3116,15 @@ export class PersonService {
3086
3116
  return;
3087
3117
  }
3088
3118
 
3089
- const fromStageSql = fromStage
3090
- ? Prisma.sql`CAST(${fromStage} AS crm_stage_history_from_stage_enum)`
3091
- : Prisma.sql`NULL`;
3092
-
3093
- await tx.$executeRaw(
3094
- Prisma.sql`
3095
- INSERT INTO crm_stage_history (
3096
- person_id,
3097
- from_stage,
3098
- to_stage,
3099
- changed_by_user_id,
3100
- changed_at,
3101
- created_at,
3102
- updated_at
3103
- )
3104
- VALUES (
3105
- ${personId},
3106
- ${fromStageSql},
3107
- CAST(${toStage} AS crm_stage_history_to_stage_enum),
3108
- ${changedByUserId},
3109
- NOW(),
3110
- NOW(),
3111
- NOW()
3112
- )
3113
- `,
3114
- );
3119
+ await client.crm_stage_history.create({
3120
+ data: {
3121
+ person_id: personId,
3122
+ from_stage: fromStage ?? null,
3123
+ to_stage: toStage,
3124
+ changed_by_user_id: changedByUserId,
3125
+ changed_at: new Date(),
3126
+ },
3127
+ });
3115
3128
  }
3116
3129
 
3117
3130
  private async syncPersonSubtypeData(
@@ -4047,69 +4060,59 @@ export class PersonService {
4047
4060
  actorUserId: number | null;
4048
4061
  },
4049
4062
  ) {
4050
- const existingRows = (await tx.$queryRaw(
4051
- Prisma.sql`
4052
- SELECT id
4053
- FROM crm_activity
4054
- WHERE person_id = ${personId}
4055
- AND source_kind = 'followup'
4056
- AND completed_at IS NULL
4057
- ORDER BY id DESC
4058
- LIMIT 1
4059
- `,
4060
- )) as Array<{ id: number }>;
4061
- const existing = existingRows[0];
4063
+ const existing = await tx.crm_activity.findFirst({
4064
+ where: {
4065
+ person_id: personId,
4066
+ source_kind: 'followup',
4067
+ completed_at: null,
4068
+ },
4069
+ orderBy: {
4070
+ id: 'desc',
4071
+ },
4072
+ });
4062
4073
 
4063
4074
  const normalizedNotes = this.normalizeTextOrNull(notes);
4075
+ const subject = this.getFollowupActivitySubject();
4076
+ const dueAtDate = new Date(dueAt);
4077
+ const priority: CrmActivityPriority = 'medium';
4078
+ const type: CrmActivityType = 'task';
4064
4079
 
4065
4080
  if (existing) {
4066
- await tx.$executeRaw(
4067
- Prisma.sql`
4068
- UPDATE crm_activity
4069
- SET
4070
- owner_user_id = ${ownerUserId},
4071
- type = CAST(${'task'} AS crm_activity_type_enum),
4072
- subject = ${this.getFollowupActivitySubject()},
4073
- notes = ${normalizedNotes},
4074
- due_at = CAST(${dueAt} AS TIMESTAMPTZ),
4075
- priority = CAST(${'medium'} AS crm_activity_priority_enum),
4076
- updated_at = NOW()
4077
- WHERE id = ${existing.id}
4078
- `,
4079
- );
4080
- return;
4081
- }
4082
-
4083
- await tx.$executeRaw(
4084
- Prisma.sql`
4085
- INSERT INTO crm_activity (
4086
- person_id,
4087
- owner_user_id,
4088
- created_by_user_id,
4081
+ await tx.crm_activity.update({
4082
+ where: {
4083
+ id: existing.id,
4084
+ },
4085
+ data: {
4086
+ owner_user_id: ownerUserId,
4089
4087
  type,
4090
4088
  subject,
4091
- notes,
4092
- due_at,
4089
+ notes: normalizedNotes,
4090
+ due_at: dueAtDate,
4093
4091
  priority,
4094
- source_kind,
4095
- created_at,
4096
- updated_at
4097
- )
4098
- VALUES (
4099
- ${personId},
4100
- ${ownerUserId},
4101
- ${actorUserId},
4102
- CAST(${'task'} AS crm_activity_type_enum),
4103
- ${this.getFollowupActivitySubject()},
4104
- ${normalizedNotes},
4105
- CAST(${dueAt} AS TIMESTAMPTZ),
4106
- CAST(${'medium'} AS crm_activity_priority_enum),
4107
- CAST(${'followup'} AS crm_activity_source_kind_enum),
4108
- NOW(),
4109
- NOW()
4110
- )
4111
- `,
4112
- );
4092
+ updated_at: new Date(),
4093
+ },
4094
+ });
4095
+ return;
4096
+ }
4097
+
4098
+ const sourceKind: CrmActivitySourceKind = 'followup';
4099
+ const now = new Date();
4100
+
4101
+ await tx.crm_activity.create({
4102
+ data: {
4103
+ person_id: personId,
4104
+ owner_user_id: ownerUserId,
4105
+ created_by_user_id: actorUserId,
4106
+ type,
4107
+ subject,
4108
+ notes: normalizedNotes,
4109
+ due_at: dueAtDate,
4110
+ priority,
4111
+ source_kind: sourceKind,
4112
+ created_at: now,
4113
+ updated_at: now,
4114
+ },
4115
+ });
4113
4116
  }
4114
4117
 
4115
4118
  private async createCompletedInteractionActivity(
@@ -4127,41 +4130,27 @@ export class PersonService {
4127
4130
  },
4128
4131
  ) {
4129
4132
  const completedAt = new Date(interaction.created_at);
4133
+ const type: CrmActivityType = interaction.type;
4134
+ const priority: CrmActivityPriority = 'medium';
4135
+ const sourceKind: CrmActivitySourceKind = 'interaction';
4130
4136
 
4131
- await tx.$executeRaw(
4132
- Prisma.sql`
4133
- INSERT INTO crm_activity (
4134
- person_id,
4135
- owner_user_id,
4136
- created_by_user_id,
4137
- completed_by_user_id,
4138
- type,
4139
- subject,
4140
- notes,
4141
- due_at,
4142
- completed_at,
4143
- priority,
4144
- source_kind,
4145
- created_at,
4146
- updated_at
4147
- )
4148
- VALUES (
4149
- ${personId},
4150
- ${ownerUserId},
4151
- ${actorUserId},
4152
- ${actorUserId},
4153
- CAST(${interaction.type} AS crm_activity_type_enum),
4154
- ${this.getInteractionActivitySubject(interaction.type)},
4155
- ${this.normalizeTextOrNull(interaction.notes)},
4156
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
4157
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
4158
- CAST(${'medium'} AS crm_activity_priority_enum),
4159
- CAST(${'interaction'} AS crm_activity_source_kind_enum),
4160
- CAST(${interaction.created_at} AS TIMESTAMPTZ),
4161
- NOW()
4162
- )
4163
- `,
4164
- );
4137
+ await tx.crm_activity.create({
4138
+ data: {
4139
+ person_id: personId,
4140
+ owner_user_id: ownerUserId,
4141
+ created_by_user_id: actorUserId,
4142
+ completed_by_user_id: actorUserId,
4143
+ type,
4144
+ subject: this.getInteractionActivitySubject(interaction.type),
4145
+ notes: this.normalizeTextOrNull(interaction.notes),
4146
+ due_at: completedAt,
4147
+ completed_at: completedAt,
4148
+ priority,
4149
+ source_kind: sourceKind,
4150
+ created_at: completedAt,
4151
+ updated_at: new Date(),
4152
+ },
4153
+ });
4165
4154
  }
4166
4155
 
4167
4156
  private getFollowupActivitySubject() {
@@ -4494,7 +4483,7 @@ export class PersonService {
4494
4483
 
4495
4484
  if (lifecycleStage && lifecycleStage !== 'all') {
4496
4485
  filters.push(
4497
- Prisma.sql`AND pc.account_lifecycle_stage = CAST(${lifecycleStage} AS person_company_account_lifecycle_stage_enum)`,
4486
+ Prisma.sql`AND pc.account_lifecycle_stage = ${lifecycleStage}`,
4498
4487
  );
4499
4488
  }
4500
4489
 
@@ -4602,12 +4591,12 @@ export class PersonService {
4602
4591
  }
4603
4592
 
4604
4593
  if (type && type !== 'all') {
4605
- filters.push(Prisma.sql`AND a.type = CAST(${type} AS crm_activity_type_enum)`);
4594
+ filters.push(Prisma.sql`AND a.type = ${type}`);
4606
4595
  }
4607
4596
 
4608
4597
  if (priority && priority !== 'all') {
4609
4598
  filters.push(
4610
- Prisma.sql`AND a.priority = CAST(${priority} AS crm_activity_priority_enum)`,
4599
+ Prisma.sql`AND a.priority = ${priority}`,
4611
4600
  );
4612
4601
  }
4613
4602
 
@@ -0,0 +1,196 @@
1
+ /// <reference types="jest" />
2
+ import { ProposalService } from './proposal.service';
3
+
4
+ describe('ProposalService regression coverage', () => {
5
+ let prisma: any;
6
+ let service: ProposalService;
7
+
8
+ beforeEach(() => {
9
+ prisma = {
10
+ proposal: {
11
+ findMany: jest.fn().mockImplementation(({ include }) => {
12
+ if (
13
+ include?.person?.select?.trade_name ||
14
+ include?.person?.select?.email ||
15
+ include?.person?.select?.phone
16
+ ) {
17
+ throw new Error(
18
+ 'Unknown field selected directly on model `person`.',
19
+ );
20
+ }
21
+
22
+ return Promise.resolve([
23
+ {
24
+ id: 101,
25
+ person_id: 8,
26
+ person: {
27
+ id: 8,
28
+ name: 'Ana Cliente',
29
+ email: 'contato@cliente.test',
30
+ phone: '+55 11 99999-0000',
31
+ },
32
+ proposal_revision: [],
33
+ },
34
+ ]);
35
+ }),
36
+ count: jest.fn().mockResolvedValue(1),
37
+ aggregate: jest.fn().mockResolvedValue({
38
+ _sum: {
39
+ total_amount_cents: 0,
40
+ },
41
+ }),
42
+ },
43
+ person_company: {
44
+ findMany: jest.fn().mockResolvedValue([
45
+ {
46
+ id: 8,
47
+ trade_name: 'Cliente SA',
48
+ },
49
+ ]),
50
+ },
51
+ contact: {
52
+ findMany: jest.fn().mockResolvedValue([
53
+ {
54
+ id: 1,
55
+ person_id: 8,
56
+ value: 'contato@cliente.test',
57
+ is_primary: true,
58
+ contact_type: {
59
+ code: 'EMAIL',
60
+ },
61
+ },
62
+ {
63
+ id: 2,
64
+ person_id: 8,
65
+ value: '+55 11 99999-0000',
66
+ is_primary: true,
67
+ contact_type: {
68
+ code: 'PHONE',
69
+ },
70
+ },
71
+ ]),
72
+ },
73
+ document: {
74
+ findMany: jest.fn().mockResolvedValue([
75
+ {
76
+ id: 3,
77
+ person_id: 8,
78
+ value: '12.345.678/0001-90',
79
+ },
80
+ ]),
81
+ },
82
+ person_metadata: {
83
+ findFirst: jest.fn().mockResolvedValue(null),
84
+ update: jest.fn().mockResolvedValue(null),
85
+ create: jest.fn().mockResolvedValue(null),
86
+ delete: jest.fn().mockResolvedValue(null),
87
+ },
88
+ $transaction: jest.fn(),
89
+ };
90
+
91
+ service = new ProposalService(
92
+ prisma,
93
+ {
94
+ findLinksBySource: jest.fn(),
95
+ publishEvent: jest.fn(),
96
+ } as any,
97
+ {
98
+ upload: jest.fn(),
99
+ } as any,
100
+ {
101
+ getSettingValues: jest.fn(),
102
+ } as any,
103
+ );
104
+ });
105
+
106
+ it('lists proposals without selecting a missing person.trade_name column', async () => {
107
+ const result = await service.list({
108
+ search: 'cliente',
109
+ take: 20,
110
+ skip: 0,
111
+ pageSize: 20,
112
+ } as any);
113
+
114
+ expect(prisma.proposal.findMany).toHaveBeenCalledWith(
115
+ expect.objectContaining({
116
+ include: expect.objectContaining({
117
+ person: expect.objectContaining({
118
+ select: expect.not.objectContaining({
119
+ trade_name: true,
120
+ }),
121
+ }),
122
+ }),
123
+ }),
124
+ );
125
+ expect(prisma.person_company.findMany).toHaveBeenCalledWith({
126
+ where: {
127
+ id: {
128
+ in: [8],
129
+ },
130
+ },
131
+ select: {
132
+ id: true,
133
+ trade_name: true,
134
+ },
135
+ });
136
+ expect(prisma.contact.findMany).toHaveBeenCalled();
137
+ expect(prisma.document.findMany).toHaveBeenCalled();
138
+ expect(result.data).toEqual(
139
+ expect.arrayContaining([
140
+ expect.objectContaining({
141
+ person: expect.objectContaining({
142
+ trade_name: 'Cliente SA',
143
+ email: 'contato@cliente.test',
144
+ phone: '+55 11 99999-0000',
145
+ document: '12.345.678/0001-90',
146
+ }),
147
+ }),
148
+ ]),
149
+ );
150
+ });
151
+
152
+ it('syncs person deal value metadata from approved and converted proposal totals', async () => {
153
+ prisma.proposal.aggregate.mockResolvedValue({
154
+ _sum: {
155
+ total_amount_cents: 125050,
156
+ },
157
+ });
158
+ prisma.person_metadata.findFirst.mockResolvedValue({ id: 77 });
159
+
160
+ await (service as any).syncPersonDealValueFromApprovedProposals(prisma, 8);
161
+
162
+ expect(prisma.proposal.aggregate).toHaveBeenCalledWith({
163
+ where: {
164
+ deleted_at: null,
165
+ person_id: 8,
166
+ status: {
167
+ in: ['approved', 'contract_generated'],
168
+ },
169
+ },
170
+ _sum: {
171
+ total_amount_cents: true,
172
+ },
173
+ });
174
+ expect(prisma.person_metadata.update).toHaveBeenCalledWith({
175
+ where: { id: 77 },
176
+ data: { value: '1250.50' },
177
+ });
178
+ });
179
+
180
+ it('clears person deal value metadata when there are no approved proposals left', async () => {
181
+ prisma.proposal.aggregate.mockResolvedValue({
182
+ _sum: {
183
+ total_amount_cents: 0,
184
+ },
185
+ });
186
+ prisma.person_metadata.findFirst.mockResolvedValue({ id: 88 });
187
+
188
+ await (service as any).syncPersonDealValueFromApprovedProposals(prisma, 8);
189
+
190
+ expect(prisma.person_metadata.delete).toHaveBeenCalledWith({
191
+ where: { id: 88 },
192
+ });
193
+ expect(prisma.person_metadata.create).not.toHaveBeenCalled();
194
+ expect(prisma.person_metadata.update).not.toHaveBeenCalled();
195
+ });
196
+ });