Skip to content

Commit d7d3012

Browse files
committed
Properly use db transaction context
1 parent 54f823d commit d7d3012

File tree

4 files changed

+140
-112
lines changed

4 files changed

+140
-112
lines changed

src/api/repository/identity-verification.repo.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { verification_status } from '@prisma/client';
2+
import { Prisma, verification_status } from '@prisma/client';
33
import { PrismaService } from 'src/shared/global/prisma.service';
44

55
@Injectable()
@@ -12,14 +12,18 @@ export class IdentityVerificationRepository {
1212
* @param userId - The unique identifier of the user.
1313
* @returns A promise that resolves to `true` if the user has at least one active identity verification association, otherwise `false`.
1414
*/
15-
async completedIdentityVerification(userId: string): Promise<boolean> {
16-
const count =
17-
await this.prisma.user_identity_verification_associations.count({
18-
where: {
19-
user_id: userId,
20-
verification_status: verification_status.ACTIVE,
21-
},
22-
});
15+
async completedIdentityVerification(
16+
userId: string,
17+
tx?: Prisma.TransactionClient,
18+
): Promise<boolean> {
19+
const count = await (
20+
tx || this.prisma
21+
).user_identity_verification_associations.count({
22+
where: {
23+
user_id: userId,
24+
verification_status: verification_status.ACTIVE,
25+
},
26+
});
2327

2428
return count > 0;
2529
}

src/api/repository/paymentMethod.repo.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { Injectable } from '@nestjs/common';
2-
import { payment_method_status, user_payment_methods } from '@prisma/client';
2+
import {
3+
payment_method_status,
4+
Prisma,
5+
user_payment_methods,
6+
} from '@prisma/client';
37
import { PrismaService } from 'src/shared/global/prisma.service';
48

59
@Injectable()
@@ -14,14 +18,16 @@ export class PaymentMethodRepository {
1418
*/
1519
async getConnectedPaymentMethod(
1620
userId: string,
21+
tx?: Prisma.TransactionClient,
1722
): Promise<user_payment_methods | null> {
18-
const connectedUserPaymentMethod =
19-
await this.prisma.user_payment_methods.findFirst({
20-
where: {
21-
user_id: userId,
22-
status: payment_method_status.CONNECTED,
23-
},
24-
});
23+
const connectedUserPaymentMethod = await (
24+
tx || this.prisma
25+
).user_payment_methods.findFirst({
26+
where: {
27+
user_id: userId,
28+
status: payment_method_status.CONNECTED,
29+
},
30+
});
2531

2632
return connectedUserPaymentMethod;
2733
}

src/api/repository/taxForm.repo.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Injectable } from '@nestjs/common';
2-
import { tax_form_status } from '@prisma/client';
2+
import { Prisma, tax_form_status } from '@prisma/client';
33
import { PrismaService } from 'src/shared/global/prisma.service';
44

55
@Injectable()
@@ -12,8 +12,11 @@ export class TaxFormRepository {
1212
* @param userId user id
1313
* @returns true if user has active tax form
1414
*/
15-
async hasActiveTaxForm(userId: string): Promise<boolean> {
16-
const count = await this.prisma.user_tax_form_associations.count({
15+
async hasActiveTaxForm(
16+
userId: string,
17+
tx?: Prisma.TransactionClient,
18+
): Promise<boolean> {
19+
const count = await (tx || this.prisma).user_tax_form_associations.count({
1720
where: {
1821
user_id: userId,
1922
tax_form_status: tax_form_status.ACTIVE,

src/api/winnings/winnings.service.ts

Lines changed: 107 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,13 @@ export class WinningsService {
8888
}
8989
}
9090

91-
private async setPayrollPaymentMethod(userId: string) {
92-
const payrollPaymentMethod = await this.prisma.payment_method.findFirst({
91+
private async setPayrollPaymentMethod(
92+
userId: string,
93+
tx?: Prisma.TransactionClient,
94+
) {
95+
const payrollPaymentMethod = await (
96+
tx || this.prisma
97+
).payment_method.findFirst({
9398
where: {
9499
payment_method_type: 'Wipro Payroll',
95100
},
@@ -101,7 +106,7 @@ export class WinningsService {
101106
}
102107

103108
if (
104-
await this.prisma.user_payment_methods.findFirst({
109+
await (tx || this.prisma).user_payment_methods.findFirst({
105110
where: {
106111
user_id: userId,
107112
payment_method_id: payrollPaymentMethod.payment_method_id,
@@ -146,110 +151,120 @@ export class WinningsService {
146151
body,
147152
);
148153

149-
const originId = await this.originRepo.getOriginIdByName(body.origin);
154+
return this.prisma.$transaction(async (tx) => {
155+
const originId = await this.originRepo.getOriginIdByName(body.origin, tx);
150156

151-
if (!originId) {
152-
this.logger.warn('Invalid origin provided', { originId });
157+
if (!originId) {
158+
this.logger.warn('Invalid origin provided', { originId });
153159

154-
result.error = {
155-
code: HttpStatus.BAD_REQUEST,
156-
message: 'Origin name does not exist',
157-
};
158-
return result;
159-
}
160+
result.error = {
161+
code: HttpStatus.BAD_REQUEST,
162+
message: 'Origin name does not exist',
163+
};
164+
return result;
165+
}
160166

161-
const winningModel = {
162-
winner_id: body.winnerId,
163-
type: body.type,
164-
origin_id: originId,
165-
category: body.category,
166-
title: body.title,
167-
description: body.description,
168-
external_id: body.externalId,
169-
attributes: body.attributes,
170-
created_by: userId,
171-
payment: {
172-
create: [] as Partial<payment>[],
173-
},
174-
};
167+
const winningModel = {
168+
winner_id: body.winnerId,
169+
type: body.type,
170+
origin_id: originId,
171+
category: body.category,
172+
title: body.title,
173+
description: body.description,
174+
external_id: body.externalId,
175+
attributes: body.attributes,
176+
created_by: userId,
177+
payment: {
178+
create: [] as Partial<payment>[],
179+
},
180+
};
175181

176-
this.logger.debug('Constructed winning model', { winningModel });
182+
this.logger.debug('Constructed winning model', { winningModel });
177183

178-
const payrollPayment = (body.attributes || {})['payroll'] === true;
184+
const payrollPayment = (body.attributes || {})['payroll'] === true;
179185

180-
const hasActiveTaxForm = await this.taxFormRepo.hasActiveTaxForm(
181-
body.winnerId,
182-
);
183-
const hasConnectedPaymentMethod = Boolean(
184-
await this.paymentMethodRepo.getConnectedPaymentMethod(body.winnerId),
185-
);
186-
const isIdentityVerified =
187-
await this.identityVerificationRepo.completedIdentityVerification(
186+
const hasActiveTaxForm = await this.taxFormRepo.hasActiveTaxForm(
188187
body.winnerId,
188+
tx,
189189
);
190-
191-
for (const detail of body.details || []) {
192-
const paymentModel = {
193-
gross_amount: Prisma.Decimal(detail.grossAmount),
194-
total_amount: Prisma.Decimal(detail.totalAmount),
195-
installment_number: detail.installmentNumber,
196-
currency: detail.currency,
197-
net_amount: Prisma.Decimal(0),
198-
payment_status: '' as payment_status,
199-
created_by: userId,
200-
billing_account: detail.billingAccount,
201-
challenge_fee: Prisma.Decimal(detail.challengeFee),
202-
};
203-
204-
paymentModel.net_amount = Prisma.Decimal(detail.grossAmount);
205-
paymentModel.payment_status =
206-
hasConnectedPaymentMethod && hasActiveTaxForm && isIdentityVerified
207-
? PaymentStatus.OWED
208-
: PaymentStatus.ON_HOLD;
209-
210-
if (payrollPayment) {
211-
this.logger.debug(
212-
`Payroll payment detected. Setting payment status to PAID for user ${body.winnerId}`,
190+
const hasConnectedPaymentMethod = Boolean(
191+
await this.paymentMethodRepo.getConnectedPaymentMethod(
192+
body.winnerId,
193+
tx,
194+
),
195+
);
196+
const isIdentityVerified =
197+
await this.identityVerificationRepo.completedIdentityVerification(
198+
body.winnerId,
199+
tx,
213200
);
214-
paymentModel.payment_status = PaymentStatus.PAID;
215-
await this.setPayrollPaymentMethod(body.winnerId);
201+
202+
for (const detail of body.details || []) {
203+
const paymentModel = {
204+
gross_amount: Prisma.Decimal(detail.grossAmount),
205+
total_amount: Prisma.Decimal(detail.totalAmount),
206+
installment_number: detail.installmentNumber,
207+
currency: detail.currency,
208+
net_amount: Prisma.Decimal(0),
209+
payment_status: '' as payment_status,
210+
created_by: userId,
211+
billing_account: detail.billingAccount,
212+
challenge_fee: Prisma.Decimal(detail.challengeFee),
213+
};
214+
215+
paymentModel.net_amount = Prisma.Decimal(detail.grossAmount);
216+
paymentModel.payment_status =
217+
hasConnectedPaymentMethod && hasActiveTaxForm && isIdentityVerified
218+
? PaymentStatus.OWED
219+
: PaymentStatus.ON_HOLD;
220+
221+
if (payrollPayment) {
222+
this.logger.debug(
223+
`Payroll payment detected. Setting payment status to PAID for user ${body.winnerId}`,
224+
);
225+
paymentModel.payment_status = PaymentStatus.PAID;
226+
await this.setPayrollPaymentMethod(body.winnerId, tx);
227+
}
228+
229+
winningModel.payment.create.push(paymentModel);
230+
this.logger.debug('Added payment model to winning model', {
231+
paymentModel,
232+
});
216233
}
217234

218-
winningModel.payment.create.push(paymentModel);
219-
this.logger.debug('Added payment model to winning model', {
220-
paymentModel,
235+
this.logger.debug('Attempting to create winning with nested payments.');
236+
const createdWinning = await tx.winnings.create({
237+
data: winningModel as any,
221238
});
222-
}
223-
224-
this.logger.debug('Attempting to create winning with nested payments.');
225-
const createdWinning = await this.prisma.winnings.create({
226-
data: winningModel as any,
227-
});
228-
229-
if (!createdWinning) {
230-
this.logger.error('Failed to create winning!');
231-
result.error = {
232-
code: HttpStatus.INTERNAL_SERVER_ERROR,
233-
message: 'Failed to create winning!',
234-
};
235-
} else {
236-
this.logger.debug('Successfully created winning', { createdWinning });
237-
}
238239

239-
if (!payrollPayment && (!hasConnectedPaymentMethod || !hasActiveTaxForm)) {
240-
const amount = body.details.find(
241-
(d) => d.installmentNumber === 1,
242-
)?.totalAmount;
240+
if (!createdWinning) {
241+
this.logger.error('Failed to create winning!');
242+
result.error = {
243+
code: HttpStatus.INTERNAL_SERVER_ERROR,
244+
message: 'Failed to create winning!',
245+
};
246+
} else {
247+
this.logger.debug('Successfully created winning', { createdWinning });
248+
}
243249

244-
if (amount) {
245-
this.logger.debug(
246-
`Sending setup email notification for user ${body.winnerId} with amount ${amount}`,
247-
);
248-
void this.sendSetupEmailNotification(body.winnerId, amount);
250+
if (
251+
!payrollPayment &&
252+
(!hasConnectedPaymentMethod || !hasActiveTaxForm)
253+
) {
254+
const amount = body.details.find(
255+
(d) => d.installmentNumber === 1,
256+
)?.totalAmount;
257+
258+
if (amount) {
259+
this.logger.debug(
260+
`Sending setup email notification for user ${body.winnerId} with amount ${amount}`,
261+
);
262+
void this.sendSetupEmailNotification(body.winnerId, amount);
263+
}
249264
}
250-
}
251265

252-
this.logger.debug('Transaction completed successfully.');
253-
return result;
266+
this.logger.debug('Transaction completed successfully.');
267+
return result;
268+
});
254269
}
255270
}

0 commit comments

Comments
 (0)