This commit is contained in:
2025-11-20 17:33:54 +05:30
2 changed files with 66 additions and 23 deletions

View File

@@ -78,29 +78,14 @@ export const handler = safeHandler(async (
throw new ApiError(400, 'Per value must be greater than 0');
}
// Run user creation, revenue and invite details inside a transaction
const createdUser = await prismaService.$transaction(async (tx) => {
// create a transaction-scoped MinglarService instance
const txMinglarService = new MinglarService(tx as unknown as PrismaService);
// Check if user already exists within transaction to avoid race
const existingUser = await txMinglarService.checkUserExists(emailAddress);
if (existingUser) {
throw new ApiError(400, 'User already exists.');
}
// Create new user
const user = await txMinglarService.createUserForInvite(emailAddress, roleXid);
// Create user revenue
await txMinglarService.createUserRevenue(user.id, isFixedSalary, perValue || 0);
// Create invite details
await txMinglarService.createInviteDetails(user.id, userInfo.id, MINGLAR_INVITATION_STATUS.INVITED);
// return created user from transaction
return user;
});
// Use single service method that encapsulates the transaction
const createdUser = await minglarService.inviteTeammate(
emailAddress,
roleXid,
isFixedSalary,
perValue || 0,
userInfo.id
);
// send email after transaction commits
await sendInvitationEmailForMinglarAdmin(emailAddress);

View File

@@ -192,6 +192,64 @@ export class MinglarService {
});
}
/**
* Invite teammate flow: checks existing user, creates user, revenue and invite details
* All operations are performed inside a single DB transaction to avoid races.
*/
async inviteTeammate(
emailAddress: string,
roleXid: number,
isFixedSalary: boolean,
perValue: number,
invitedBy: number
) {
return await this.prisma.$transaction(async (tx) => {
// Check existing user
const existingUser = await tx.user.findFirst({
where: { emailAddress: emailAddress, isActive: true },
});
if (existingUser) {
throw new ApiError(400, 'User already exists.');
}
// Create user with INVITED status
const user = await tx.user.create({
data: {
emailAddress: emailAddress,
roleXid: roleXid,
userStatus: USER_STATUS.INVITED,
},
});
// Create revenue record
await tx.userRevenue.create({
data: {
userXid: user.id,
is_fixed_salary: isFixedSalary,
per_value: perValue || 0,
isActive: true,
},
});
// Create invite details
await tx.inviteDetails.create({
data: {
userXid: user.id,
is_invited: true,
invited_by: invitedBy,
invited_on: new Date(),
is_accepted: false,
invitation_status: MINGLAR_INVITATION_STATUS.INVITED,
isActive: true,
isMinglarInvitation: true,
},
});
return user;
});
}
async updateProfile(
userId: number,
userData: {