diff --git a/app/Domains/Auth/Models/User.php b/app/Domains/Auth/Models/User.php index 1826676..8a3979b 100644 --- a/app/Domains/Auth/Models/User.php +++ b/app/Domains/Auth/Models/User.php @@ -19,7 +19,7 @@ use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; #[Fillable(['nombre_apellido', 'email', 'password', 'dni', 'telefono', 'google_id', 'rol_codigo', 'tenant_codigo'])] -#[Hidden(['password', 'remember_token', 'active_email'])] +#[Hidden(['password', 'remember_token', 'active_email', 'active_google_id'])] class User extends Authenticatable { /** @use HasFactory */ diff --git a/app/Domains/Desfile/Services/InvitationPurchaseProvisioner.php b/app/Domains/Desfile/Services/InvitationPurchaseProvisioner.php index bc2c986..c30015c 100644 --- a/app/Domains/Desfile/Services/InvitationPurchaseProvisioner.php +++ b/app/Domains/Desfile/Services/InvitationPurchaseProvisioner.php @@ -104,7 +104,10 @@ class InvitationPurchaseProvisioner private function userId(DateTimeInterface $now): int { - $user = DB::table('users')->where('email', self::USER_EMAIL)->first(); + $user = DB::table('users') + ->where('email', self::USER_EMAIL) + ->whereNull('deleted_at') + ->first(); if ($user !== null) { if ($user->tenant_codigo !== self::TENANT_CODE) { diff --git a/database/migrations/2026_09_03_000200_scope_google_id_unique_to_active_accounts.php b/database/migrations/2026_09_03_000200_scope_google_id_unique_to_active_accounts.php new file mode 100644 index 0000000..1ee6159 --- /dev/null +++ b/database/migrations/2026_09_03_000200_scope_google_id_unique_to_active_accounts.php @@ -0,0 +1,28 @@ +dropUnique(['google_id']); + $table->string('active_google_id') + ->nullable() + ->storedAs('CASE WHEN `deleted_at` IS NULL THEN `google_id` ELSE NULL END'); + $table->unique('active_google_id'); + }); + } + + public function down(): void + { + Schema::table('users', function (Blueprint $table): void { + $table->dropUnique(['active_google_id']); + $table->dropColumn('active_google_id'); + $table->unique('google_id'); + }); + } +}; diff --git a/tests/Feature/Auth/RegisterControllerTest.php b/tests/Feature/Auth/RegisterControllerTest.php index 2580c61..8dafa91 100644 --- a/tests/Feature/Auth/RegisterControllerTest.php +++ b/tests/Feature/Auth/RegisterControllerTest.php @@ -135,4 +135,36 @@ class RegisterControllerTest extends TestCase 'password', ]); } + + public function test_it_can_reuse_the_email_of_a_soft_deleted_user(): void + { + $deletedUser = User::factory()->create([ + 'email' => 'reused@example.com', + ]); + $deletedUser->delete(); + + $response = $this->postJson('/api/register', [ + 'nombre_apellido' => 'New Account', + 'email' => 'reused@example.com', + 'password' => 'Secret!123', + 'password_confirmation' => 'Secret!123', + ])->assertCreated(); + + $newUserId = $response->json('data.id'); + + $this->assertNotSame($deletedUser->id, $newUserId); + $this->assertSame( + 2, + User::withTrashed()->where('email', 'reused@example.com')->count(), + ); + $this->assertDatabaseHas('users', [ + 'id' => $deletedUser->id, + 'active_email' => null, + ]); + $this->assertDatabaseHas('users', [ + 'id' => $newUserId, + 'active_email' => 'reused@example.com', + 'deleted_at' => null, + ]); + } }