f = paymentFixture(); config(['common-payables.enabled' => true, 'common-payables.source_instance' => 'integration-fixture-instance']); foreach ([$this->f['author'], $this->f['validator']] as $user) { foreach (['view', 'prepare', 'validate'] as $action) { DB::table('pa_access')->insert(['tenant_id' => $user->tenant_id, 'company_id' => 0, 'unit_id' => 0, 'user_id' => $user->id, 'person' => 'fixture-person-'.$user->id, 'action' => $action]); } } $this->actingAs($this->f['author']); }); test('explicit activation recognises a confirmed invoice once on the normal application schema', function () { expect(SupplierPayables::enabled())->toBeTrue(); $service = app(SupplierPayables::class); $prepared = $service->engine($this->f['author'])->command('prepare', $this->f['invoice']->id, 'Conferência de integração sintética', (string) Str::uuid()); expect($prepared['state'])->toBe('prepared'); $this->actingAs($this->f['validator']); $recognised = $service->engine($this->f['validator'])->command('validate', $prepared['proposal_id'], 'Validação independente de teste', (string) Str::uuid()); expect($recognised['state'])->toBe('recognised'); $service->engine($this->f['validator'])->command('validate', $prepared['proposal_id'], 'Validação repetida de teste', (string) Str::uuid()); expect(DB::table('pa_obligations')->count())->toBe(1) ->and(DB::table('pa_obligations')->value('principal_minor'))->toBe(6000) ->and(DB::table('pa_obligations')->value('due_on'))->toBeNull() ->and(app(ExternalPaymentWorkflow::class)->outstanding($this->f['invoice']))->toBe(6000); $this->getJson('/api/supplier-payables?invoice='.$this->f['invoice']->id)->assertOk()->assertJsonCount(1, 'obligations'); config(['common-payables.source_instance' => 'different-installation']); expect(fn () => $service->principal($this->f['invoice']))->toThrow(HttpException::class); }); test('legacy payments keep their balance and prevent automatic import of a second debt', function () { $payments = app(ExternalPaymentWorkflow::class); $payment = $payments->register($this->f['author'], $this->f['invoice'], $this->f['payment_data']); $this->actingAs($this->f['validator']); $payments->review($this->f['validator'], $payment, 'confirmed', 'Conferência sintética do histórico'); expect($payments->outstanding($this->f['invoice']))->toBe(2000); $this->actingAs($this->f['author']); $prepared = app(SupplierPayables::class)->engine($this->f['author'])->command('prepare', $this->f['invoice']->id, 'Tentativa com pagamento anterior', (string) Str::uuid()); expect($prepared['state'])->toBe('pending_source'); $this->actingAs($this->f['validator']); $result = app(SupplierPayables::class)->engine($this->f['validator'])->command('validate', $prepared['proposal_id'], 'Rever histórico sem nova dívida', (string) Str::uuid()); expect($result['state'])->toBe('conflict')->and(DB::table('pa_obligations')->count())->toBe(0) ->and($payments->outstanding($this->f['invoice']))->toBe(2000); }); test('activation does not grant permissions and source queries remain tenant scoped', function () { DB::table('pa_access')->where('user_id', $this->f['author']->id)->delete(); $this->getJson('/api/supplier-payables')->assertForbidden(); $foreign = User::factory()->create(['tenant_id' => null]); $this->actingAs($foreign); expect(fn () => app(SupplierPayables::class)->principal($this->f['invoice']))->toThrow(HttpException::class); config(['common-payables.enabled' => false]); expect(SupplierPayables::enabled())->toBeFalse(); }); test('the shared schema preserves histories and refuses repeated or partial installation', function () { expect(fn () => PayablesSchema::install())->toThrow(LogicException::class); $service = app(SupplierPayables::class); $service->engine($this->f['author'])->command('prepare', $this->f['invoice']->id, 'Criar histórico imutável de teste', (string) Str::uuid()); expect(fn () => DB::table('pa_events')->delete())->toThrow(QueryException::class); }); test('navigation exposes the workspace only while the current actor has explicit view access', function () { $this->get('/dashboard')->assertOk()->assertInertia(fn ($page) => $page->where('payablesAvailable', true)); DB::table('pa_access')->where('user_id', $this->f['author']->id)->where('action', 'view')->delete(); $this->get('/dashboard')->assertOk()->assertInertia(fn ($page) => $page->where('payablesAvailable', false)); $this->get('/obrigacoes-fornecedores')->assertForbidden(); });