Skip to content

Commit d81ddc7

Browse files
simonhampclaude
andcommitted
Fix re-inviting previously removed team members
Reuse the existing Removed team_users record instead of inserting a duplicate, which violated the team_id+email unique constraint. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 80248c4 commit d81ddc7

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

app/Http/Controllers/TeamUserController.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,28 @@ public function invite(InviteTeamUserRequest $request): RedirectResponse
5858
->with('show_add_seats', true);
5959
}
6060

61-
$member = $team->users()->create([
62-
'email' => $email,
63-
'invitation_token' => bin2hex(random_bytes(32)),
64-
'invited_at' => now(),
65-
]);
61+
// Reuse existing removed record if re-inviting a previously removed member
62+
$existingRemoved = $team->users()
63+
->where('email', $email)
64+
->where('status', TeamUserStatus::Removed)
65+
->first();
66+
67+
if ($existingRemoved) {
68+
$existingRemoved->update([
69+
'status' => TeamUserStatus::Pending,
70+
'user_id' => null,
71+
'invitation_token' => bin2hex(random_bytes(32)),
72+
'invited_at' => now(),
73+
'accepted_at' => null,
74+
]);
75+
$member = $existingRemoved;
76+
} else {
77+
$member = $team->users()->create([
78+
'email' => $email,
79+
'invitation_token' => bin2hex(random_bytes(32)),
80+
'invited_at' => now(),
81+
]);
82+
}
6683

6784
Notification::route('mail', $email)
6885
->notify(new TeamInvitation($member));

tests/Feature/TeamManagementTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,38 @@ public function test_cannot_invite_duplicate_email(): void
156156
$response->assertSessionHas('error');
157157
}
158158

159+
public function test_can_reinvite_previously_removed_member(): void
160+
{
161+
Notification::fake();
162+
163+
[$owner, $team] = $this->createTeamWithOwner();
164+
165+
// Create a removed team user (previously invited then cancelled/removed)
166+
$removed = TeamUser::factory()->create([
167+
'team_id' => $team->id,
168+
'email' => 'member@example.com',
169+
'status' => TeamUserStatus::Removed,
170+
'user_id' => null,
171+
]);
172+
173+
$response = $this->actingAs($owner)
174+
->post(route('customer.team.invite'), ['email' => 'member@example.com']);
175+
176+
$response->assertSessionHas('success');
177+
178+
$this->assertDatabaseHas('team_users', [
179+
'id' => $removed->id,
180+
'team_id' => $team->id,
181+
'email' => 'member@example.com',
182+
'status' => TeamUserStatus::Pending->value,
183+
]);
184+
185+
// Should reuse the existing record, not create a new one
186+
$this->assertEquals(1, TeamUser::where('team_id', $team->id)->where('email', 'member@example.com')->count());
187+
188+
Notification::assertSentOnDemand(TeamInvitation::class);
189+
}
190+
159191
public function test_cannot_invite_when_team_suspended(): void
160192
{
161193
Notification::fake();

0 commit comments

Comments
 (0)