From b91799bdcfcf43a72083aea2afa01a9e10b15d6c Mon Sep 17 00:00:00 2001 From: DudsFerraz Date: Wed, 9 Sep 2026 19:43:40 +0000 Subject: [PATCH 1/2] fix(usuarios): resolve identificadores com prefixo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Centraliza a resolução de identificadores codpes-* e id-*. Garante que o Replicado receba o codpes como inteiro e preserva a compatibilidade com identificadores numéricos antigos. Atualiza os fluxos de setores, filas, chamados e usuários e adiciona testes de regressão. --- app/Http/Controllers/ChamadoController.php | 12 +- app/Http/Controllers/FilaController.php | 16 +- app/Http/Controllers/SetorController.php | 10 +- app/Http/Controllers/UserController.php | 11 +- app/Models/User.php | 35 ++++ tests/Unit/UserIdentifierTest.php | 232 +++++++++++++++++++++ 6 files changed, 300 insertions(+), 16 deletions(-) create mode 100644 tests/Unit/UserIdentifierTest.php diff --git a/app/Http/Controllers/ChamadoController.php b/app/Http/Controllers/ChamadoController.php index b572aa66..4cc2aeb2 100644 --- a/app/Http/Controllers/ChamadoController.php +++ b/app/Http/Controllers/ChamadoController.php @@ -539,7 +539,7 @@ public function storePessoa(Request $request, Chamado $chamado) $request->validate( [ - 'codpes' => ['required', 'regex:/^((codpes|id)-)?\d+$/'], + 'codpes' => User::identificadorRules, 'papel' => 'required|in:' . implode(',', Chamado::pessoaPapeis()), ] ); @@ -552,15 +552,7 @@ public function storePessoa(Request $request, Chamado $chamado) $this->authorize('atendente'); } - if (is_numeric($codpesField)) { - $user = User::obterOuCriarPorCodpes((int) $codpesField); - } else { - [$searchField, $valueField] = explode('-', $codpesField, 2); - - $user = $searchField === 'codpes' - ? User::obterOuCriarPorCodpes((int) $valueField) - : User::find((int) $valueField); - } + $user = User::obterOuCriarPorIdentificador($codpesField); if (empty($user)) { return back()->withErrors(['codpes' => 'Usuário não encontrado.'])->withInput(); diff --git a/app/Http/Controllers/FilaController.php b/app/Http/Controllers/FilaController.php index 6090a987..a9b00f53 100644 --- a/app/Http/Controllers/FilaController.php +++ b/app/Http/Controllers/FilaController.php @@ -183,11 +183,19 @@ public function storePessoa(Request $request, Fila $fila) { $this->authorize('filas.update', $fila); - [$searchField, $valueField] = explode('-', $request->codpes_id); + // codpes era o nome usado por clientes antigos; o formulário atual usa codpes_id. + $identificador = $request->input('codpes_id', $request->input('codpes')); + $request->merge(['codpes_id' => $identificador]); + $request->validate([ + 'codpes_id' => User::identificadorRules, + 'funcao' => 'required|in:Gerente,Atendente', + ]); + + $user = User::obterOuCriarPorIdentificador($identificador); + if (empty($user)) { + return back()->withErrors(['codpes_id' => 'Usuário não encontrado.'])->withInput(); + } - $user = $searchField === 'codpes' ? - User::obterOuCriarPorCodpes($valueField) : - User::find($valueField); $fila->users()->detach($user->id); $fila->users()->attach($user->id, ['funcao' => $request->funcao]); diff --git a/app/Http/Controllers/SetorController.php b/app/Http/Controllers/SetorController.php index 46e1f491..a8e23880 100644 --- a/app/Http/Controllers/SetorController.php +++ b/app/Http/Controllers/SetorController.php @@ -136,7 +136,15 @@ public function storePessoa(Request $request, Setor $setor) { $this->authorize('setores.view', $setor); - $user = User::obterOuCriarPorCodpes($request->codpes); + $request->validate([ + 'codpes' => User::identificadorRules, + ]); + + $user = User::obterOuCriarPorIdentificador($request->codpes); + if (empty($user)) { + return back()->withErrors(['codpes' => 'Usuário não encontrado.'])->withInput(); + } + Setor::vincularPessoa($setor, $user, 'Gerente'); $request->session()->flash('alert-info', 'Pessoa adicionada com sucesso'); diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index a0fe951f..b0cbdf7a 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -50,7 +50,16 @@ public function create() public function store(Request $request) { $this->authorize('admin'); - User::obterOuCriarPorCodpes($request->codpes); + + $request->validate([ + 'codpes' => User::identificadorRules, + ]); + + $user = User::obterOuCriarPorIdentificador($request->codpes); + if (empty($user)) { + return back()->withErrors(['codpes' => 'Usuário não encontrado.'])->withInput(); + } + $request->session()->flash('alert-info', 'Atendente adicionado com sucesso'); return redirect('/users'); } diff --git a/app/Models/User.php b/app/Models/User.php index 71720780..ce68a13e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -62,6 +62,11 @@ class User extends Authenticatable 'telefone' => '', ]; + public const identificadorRules = [ + 'required', + 'regex:/^((codpes|id)-)?\d+$/', + ]; + protected const fields = [ [ 'name' => 'codpes', @@ -106,6 +111,7 @@ public static function getFields() public static function criarPorCodpes($codpes) { + $codpes = (int) $codpes; $user = new User; $user->codpes = $codpes; if (config('chamados.usar_replicado')) { @@ -131,6 +137,35 @@ public static function obterPorCodpes($codpes) return User::where('codpes', $codpes)->first(); } + /** + * Resolve um identificador vindo da busca de pessoas. + * + * Identificadores codpes-* apontam para uma pessoa do Replicado e + * identificadores id-* apontam para um usuário já existente na base local. + * Números sem prefixo são aceitos para compatibilidade com chamadas antigas. + * + * @param string|int|null $identificador + * @return User|null + */ + public static function obterOuCriarPorIdentificador($identificador) + { + $identificador = (string) $identificador; + + if (ctype_digit($identificador)) { + return self::obterOuCriarPorCodpes((int) $identificador); + } + + if (!preg_match('/^(codpes|id)-(\d+)$/', $identificador, $matches)) { + return null; + } + + $valor = (int) $matches[2]; + + return $matches[1] === 'codpes' + ? self::obterOuCriarPorCodpes($valor) + : self::find($valor); + } + /** * Obtém se já existir ou cria um novo objeto de usuário * diff --git a/tests/Unit/UserIdentifierTest.php b/tests/Unit/UserIdentifierTest.php new file mode 100644 index 00000000..68788d96 --- /dev/null +++ b/tests/Unit/UserIdentifierTest.php @@ -0,0 +1,232 @@ + false]); + } + + public function test_it_resolves_a_replication_person_by_prefixed_codpes(): void + { + $user = User::factory()->create(['codpes' => 17971882]); + + $resolved = User::obterOuCriarPorIdentificador('codpes-17971882'); + + $this->assertTrue($resolved->is($user)); + } + + public function test_it_resolves_a_local_user_by_prefixed_id(): void + { + $user = User::factory()->create(['codpes' => null, 'local' => true]); + + $resolved = User::obterOuCriarPorIdentificador('id-' . $user->id); + + $this->assertTrue($resolved->is($user)); + } + + public function test_it_creates_a_user_with_a_numeric_codpes(): void + { + $user = User::obterOuCriarPorIdentificador('codpes-17971882'); + + $this->assertSame(17971882, (int) $user->codpes); + $this->assertDatabaseHas('users', ['codpes' => 17971882]); + } + + public function test_it_passes_an_integer_codpes_to_the_replication_client(): void + { + $pessoa = \Mockery::mock('alias:Uspdev\\Replicado\\Pessoa'); + $pessoa->shouldReceive('email')->once()->with(17971882)->andReturn('pessoa@example.com'); + $pessoa->shouldReceive('dump')->once()->with(17971882)->andReturn([ + 'nompesttd' => 'Pessoa do Replicado', + ]); + $pessoa->shouldReceive('obterRamalUsp')->once()->with(17971882)->andReturn(false); + $pessoa->shouldReceive('email')->once()->with(17971883)->andReturn('outra-pessoa@example.com'); + $pessoa->shouldReceive('dump')->once()->with(17971883)->andReturn([ + 'nompesttd' => 'Outra pessoa do Replicado', + ]); + $pessoa->shouldReceive('obterRamalUsp')->once()->with(17971883)->andReturn(false); + + config(['chamados.usar_replicado' => true]); + + $user = User::obterOuCriarPorIdentificador('codpes-17971882'); + $legacyUser = User::obterOuCriarPorCodpes('17971883'); + + $this->assertSame(17971882, (int) $user->codpes); + $this->assertSame(17971883, (int) $legacyUser->codpes); + } + + public function test_it_adds_a_replication_person_as_a_sector_manager(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $target = User::factory()->create(['codpes' => 17971882]); + $setor = Setor::factory()->create(); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/setores/' . $setor->id . '/pessoas', [ + 'codpes' => 'codpes-17971882', + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('user_setor', [ + 'user_id' => $target->id, + 'setor_id' => $setor->id, + 'funcao' => 'Gerente', + ]); + $this->assertDatabaseCount('users', 2); + } + + public function test_it_adds_a_local_user_as_a_sector_manager(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $localUser = User::factory()->create(['codpes' => null, 'local' => true]); + $setor = Setor::factory()->create(); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/setores/' . $setor->id . '/pessoas', [ + 'codpes' => 'id-' . $localUser->id, + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('user_setor', [ + 'user_id' => $localUser->id, + 'setor_id' => $setor->id, + 'funcao' => 'Gerente', + ]); + $this->assertDatabaseCount('users', 2); + } + + public function test_it_rejects_an_invalid_sector_user_identifier(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $setor = Setor::factory()->create(); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/setores/' . $setor->id . '/pessoas', [ + 'codpes' => 'id-999999', + ]); + + $response->assertSessionHasErrors('codpes'); + } + + public function test_it_adds_a_local_user_to_a_queue(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $localUser = User::factory()->create(['codpes' => null, 'local' => true]); + $setor = Setor::factory()->create(); + $fila = Fila::factory()->create(['setor_id' => $setor->id]); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/filas/' . $fila->id . '/pessoas', [ + 'codpes_id' => 'id-' . $localUser->id, + 'funcao' => 'Gerente', + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('user_fila', [ + 'user_id' => $localUser->id, + 'fila_id' => $fila->id, + 'funcao' => 'Gerente', + ]); + } + + public function test_it_keeps_supporting_the_legacy_queue_codpes_field(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $target = User::factory()->create(['codpes' => 17971882]); + $setor = Setor::factory()->create(); + $fila = Fila::factory()->create(['setor_id' => $setor->id]); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/filas/' . $fila->id . '/pessoas', [ + 'codpes' => (string) $target->codpes, + 'funcao' => 'Atendente', + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('user_fila', [ + 'user_id' => $target->id, + 'fila_id' => $fila->id, + 'funcao' => 'Atendente', + ]); + } + + public function test_it_rejects_an_unknown_local_user_in_a_queue(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $setor = Setor::factory()->create(); + $fila = Fila::factory()->create(['setor_id' => $setor->id]); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/filas/' . $fila->id . '/pessoas', [ + 'codpes_id' => 'id-999999', + 'funcao' => 'Gerente', + ]); + + $response->assertSessionHasErrors('codpes_id'); + } + + public function test_it_adds_a_local_user_to_a_ticket(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $localUser = User::factory()->create(['codpes' => null, 'local' => true]); + $setor = Setor::factory()->create(); + $fila = Fila::factory()->create(['setor_id' => $setor->id]); + $chamado = Chamado::withoutEvents(function () use ($fila) { + return Chamado::factory()->create([ + 'fila_id' => $fila->id, + 'status' => 'Triagem', + ]); + }); + Mail::fake(); + + $response = $this->withSession(['perfil' => 'admin']) + ->actingAs($admin) + ->post('/chamados/' . $chamado->id . '/pessoas', [ + 'codpes' => 'id-' . $localUser->id, + 'papel' => 'Observador', + ]); + + $response->assertRedirect(); + $this->assertDatabaseHas('user_chamado', [ + 'user_id' => $localUser->id, + 'chamado_id' => $chamado->id, + 'papel' => 'Observador', + ]); + } + + public function test_it_adds_a_replication_person_from_the_users_screen(): void + { + $admin = User::factory()->create(['is_admin' => true]); + $target = User::factory()->create(['codpes' => 17971882]); + + $response = $this->actingAs($admin)->post('/users', [ + 'codpes' => 'codpes-17971882', + ]); + + $response->assertRedirect('/users'); + $this->assertDatabaseHas('users', ['id' => $target->id]); + $this->assertDatabaseCount('users', 2); + } +} From 4aa97bfcba61ed6ba8487f92ea648a87a6cbc32a Mon Sep 17 00:00:00 2001 From: Masaki Kawabata Neto Date: Thu, 10 Sep 2026 08:55:36 -0300 Subject: [PATCH 2/2] Rename test for user creation with codpes identifier Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- tests/Unit/UserIdentifierTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Unit/UserIdentifierTest.php b/tests/Unit/UserIdentifierTest.php index 68788d96..8385f969 100644 --- a/tests/Unit/UserIdentifierTest.php +++ b/tests/Unit/UserIdentifierTest.php @@ -40,7 +40,7 @@ public function test_it_resolves_a_local_user_by_prefixed_id(): void $this->assertTrue($resolved->is($user)); } - public function test_it_creates_a_user_with_a_numeric_codpes(): void + public function test_it_creates_a_user_with_a_prefixed_codpes_identifier(): void { $user = User::obterOuCriarPorIdentificador('codpes-17971882');