diff --git a/app/Http/Requests/ContractRequest.php b/app/Http/Requests/ContractRequest.php index 4af543f..4c5bccc 100644 --- a/app/Http/Requests/ContractRequest.php +++ b/app/Http/Requests/ContractRequest.php @@ -2,6 +2,7 @@ namespace App\Http\Requests; +use App\Models\Contract; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Validation\Validator; use Carbon\Carbon; @@ -96,6 +97,31 @@ class ContractRequest extends FormRequest public function withValidator(Validator $validator) { $validator->after(function ($validator) { + if ($this->filled('code') && $this->filled('supply_type')) { + $contractId = $this->route('id'); + $existingContract = Contract::query() + ->with('client:id,name') + ->where('code', $this->input('code')) + ->where('supply_type', $this->input('supply_type')) + ->when($contractId, fn ($query) => $query->where('id', '!=', $contractId)) + ->first(); + + if ($existingContract && ! $this->boolean('confirm_duplicate')) { + $supplyType = strtoupper($this->input('supply_type')); + $clientName = $existingContract->client?->name; + $message = "Já existe um contrato {$supplyType} com este código"; + + if ($clientName) { + $message .= " associado ao cliente {$clientName}"; + } + + $validator->errors()->add( + 'duplicate_code', + $message . '. Confirme apenas se se trata de uma mudança de titularidade.' + ); + } + } + if (! $this->user()?->can('view_contract_summary')) { return; } diff --git a/resources/views/web/contract/add_edit.blade.php b/resources/views/web/contract/add_edit.blade.php index 4542681..78afa90 100644 --- a/resources/views/web/contract/add_edit.blade.php +++ b/resources/views/web/contract/add_edit.blade.php @@ -635,5 +635,39 @@ applyColor('previous_supplier_select'); }); }); + + const duplicateCodeMessage = @json($errors->first('duplicate_code')); + if (duplicateCodeMessage) { + const form = document.querySelector('form'); + const submitConfirmed = () => { + let confirmation = form.querySelector('input[name="confirm_duplicate"]'); + if (!confirmation) { + confirmation = document.createElement('input'); + confirmation.type = 'hidden'; + confirmation.name = 'confirm_duplicate'; + form.appendChild(confirmation); + } + confirmation.value = '1'; + form.submit(); + }; + + if (window.Swal) { + Swal.fire({ + title: 'CPE/CUI já existente', + text: duplicateCodeMessage, + icon: 'warning', + showCancelButton: true, + confirmButtonText: 'Aceitar e gravar', + cancelButtonText: 'Cancelar', + reverseButtons: true + }).then((result) => { + if (result.isConfirmed) { + submitConfirmed(); + } + }); + } else if (window.confirm(`${duplicateCodeMessage}\n\nDeseja continuar?`)) { + submitConfirmed(); + } + } @endsection diff --git a/tests/Feature/ContractPersistenceTest.php b/tests/Feature/ContractPersistenceTest.php index eeb7d27..b4380bd 100644 --- a/tests/Feature/ContractPersistenceTest.php +++ b/tests/Feature/ContractPersistenceTest.php @@ -7,6 +7,7 @@ use App\Models\Contract; use App\Services\ContractService; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; +use Illuminate\Support\Facades\Validator; use Illuminate\Validation\ValidationException; use Tests\Support\IsolatedDatabaseTestCase; @@ -15,7 +16,38 @@ class ContractPersistenceTest extends IsolatedDatabaseTestCase protected function setUp(): void { parent::setUp(); - Schema::table('contracts', fn (Blueprint $table) => $table->decimal('go_ki')->nullable()); + Schema::table('contracts', function (Blueprint $table) { + $table->decimal('go_ki')->nullable(); + $table->string('code')->nullable(); + $table->string('supply_type')->nullable(); + }); + } + + public function test_duplicate_supply_code_requires_explicit_confirmation(): void + { + $user = $this->authenticate(); + Contract::create([ + 'client_id' => 1, + 'code' => 'CPE-123', + 'supply_type' => 'cpe', + ]); + + $request = ContractRequest::create('/contract', 'POST', [ + 'code' => 'CPE-123', + 'supply_type' => 'cpe', + ]); + $request->setUserResolver(fn () => $user); + $validator = Validator::make($request->all(), []); + $request->withValidator($validator); + + $this->assertFalse($validator->passes()); + $this->assertArrayHasKey('duplicate_code', $validator->errors()->messages()); + + $request->merge(['confirm_duplicate' => '1']); + $confirmedValidator = Validator::make($request->all(), []); + $request->withValidator($confirmedValidator); + + $this->assertTrue($confirmedValidator->passes()); } public function test_update_persists_chargeback_and_deducts_it_from_profit(): void