diff --git a/packages/lib/createAProfileForAnExistingUser.ts b/packages/lib/createAProfileForAnExistingUser.ts index c1a1e1f526..5a3b87dc37 100644 --- a/packages/lib/createAProfileForAnExistingUser.ts +++ b/packages/lib/createAProfileForAnExistingUser.ts @@ -25,6 +25,16 @@ export const createAProfileForAnExistingUser = async ({ if (!org) { throw new Error(`Organization with id ${organizationId} not found`); } + + const existingProfile = await ProfileRepository.findByUserIdAndOrgId({ + userId: user.id, + organizationId, + }); + + if (existingProfile) { + return existingProfile; + } + const usernameInOrg = getOrgUsernameFromEmail( user.email, org.organizationSettings?.orgAutoAcceptEmail ?? null diff --git a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts index 77ee3573af..87607b92ed 100644 --- a/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts +++ b/packages/trpc/server/routers/viewer/organizations/createTeams.handler.ts @@ -243,6 +243,9 @@ async function moveTeam({ language: "en", inviterName: null, teamId: org.id, + // This is important so that if we re-invite existing users accidentally, we don't endup erroring out. + // Because this is a bulk action that could be taken from organization payment webhook, we could have cases where a user was just invited through another team migration in parallel. + isDirectUserAction: false, }); } diff --git a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts index 734535f2a8..4163e338b0 100644 --- a/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/inviteMember/inviteMember.handler.ts @@ -141,16 +141,20 @@ export const inviteMembersWithNoInviterPermissionCheck = async ( role: MembershipRole; }[]; creationSource: CreationSource; + /** + * Whether invitation is a direct user action or not i.e. we need to show them User based errors like inviting existing users or not. + */ + isDirectUserAction?: boolean; } & TargetTeam ) => { - const { inviterName, orgSlug, invitations, language, creationSource } = data; + const { inviterName, orgSlug, invitations, language, creationSource, isDirectUserAction = true } = data; const myLog = log.getSubLogger({ prefix: ["inviteMembers"] }); const translation = await getTranslation(language ?? "en", "common"); const team = "team" in data ? data.team : await getTeamOrThrow(data.teamId); const isTeamAnOrg = team.isOrganization; const uniqueInvitations = await getUniqueInvitationsOrThrowIfEmpty(invitations); - const beSilentAboutErrors = shouldBeSilentAboutErrors(uniqueInvitations); + const beSilentAboutErrors = shouldBeSilentAboutErrors(uniqueInvitations) || !isDirectUserAction; const existingUsersToBeInvited = await findUsersWithInviteStatus({ invitations: uniqueInvitations, team,