actingAs($f['author'])->get('/requisicoes/'.$f['r']->id.'/processo') ->assertOk()->assertHeader('Cache-Control', 'max-age=0, no-store, private') ->assertInertia(fn (Assert $page) => $page->component('purchases/process') ->where('process.number', $f['r']->number) ->where('stages.5.state', 'issued') ->where('stages.6.state', 'recorded') ->where('stages.7.state', 'recorded') ->where('stages.7.detail', '1 registadas; 0 validadas. O registo não equivale a validação.') ->where('stages.8.state', 'not_started') ->has('future', 3) ->where('documents', fn ($documents) => collect($documents)->contains('label', $f['invoice']->number))); }); test('requester process view conceals purchase and finance details', function () { $f = invoiceFixture(); $f['issuer']->forceFill(['purchasing_role' => PurchasingRole::Requester])->save(); $this->actingAs($f['issuer'])->get('/requisicoes/'.$f['r']->id.'/processo')->assertOk() ->assertInertia(fn (Assert $page) => $page->component('purchases/process') ->where('stages.2.state', 'restricted')->where('stages.3.state', 'restricted') ->where('stages.4.state', 'restricted')->where('stages.5.state', 'restricted') ->where('stages.7.state', 'restricted')->has('documents', 1) ->where('history', fn ($history) => collect($history)->every(fn ($event) => ! str_starts_with($event['action'], 'invoice.') && ! str_starts_with($event['action'], 'budget.'))) ->missing('invoices')->missing('payments')->missing('bank_snapshot')); }); test('process view does not expand request ownership or department access', function () { $f = invoiceFixture(); $this->actingAs($f['receiver'])->get('/requisicoes/'.$f['r']->id.'/processo')->assertNotFound(); $department = Department::query()->create(['tenant_id' => $f['tenant']->id, 'name' => 'Outro departamento']); $head = User::factory()->create(['tenant_id' => $f['tenant']->id, 'department_id' => $department->id, 'purchasing_role' => PurchasingRole::DepartmentHead]); $this->actingAs($head)->get('/requisicoes/'.$f['r']->id.'/processo')->assertNotFound(); $head->forceFill(['department_id' => $f['r']->department_id])->save(); $this->actingAs($head)->get('/requisicoes/'.$f['r']->id.'/processo')->assertOk() ->assertInertia(fn (Assert $page) => $page->where('stages.3.state', 'recorded')->where('stages.7.state', 'restricted')->where('documents', fn ($documents) => ! collect($documents)->contains('kind', 'Factura'))); }); test('process view is protected across companies and for guests', function () { $f = invoiceFixture(); $otherCompany = Tenant::query()->create(['name' => 'Outra empresa fictícia']); $this->actingAs(User::factory()->create(['tenant_id' => $otherCompany->id, 'purchasing_role' => PurchasingRole::Admin]))->get('/requisicoes/'.$f['r']->id.'/processo')->assertNotFound(); auth()->logout(); $this->get('/requisicoes/'.$f['r']->id.'/processo')->assertRedirect('/login'); }); test('process overview reads current invoice state and never exposes audit snapshots', function () { $f = invoiceFixture(); $this->actingAs($f['validator']); app(InvoiceWorkflow::class)->validate($f['validator'], $f['invoice']); $before = AuditLog::query()->count(); $this->get('/requisicoes/'.$f['r']->id.'/processo')->assertOk() ->assertInertia(fn (Assert $page) => $page->where('stages.7.detail', '1 registadas; 1 validadas. O registo não equivale a validação.') ->where('history', fn ($history) => collect($history)->every(fn ($event) => array_keys($event) === ['id', 'action', 'created_at']))); expect(AuditLog::query()->count())->toBe($before); });