create(['role' => 'staff']); CustomFieldDefinition::factory(2)->create(); $this->actingAs($user)->getJson('/api/custom-fields') ->assertStatus(200) ->assertJsonCount(2, 'data'); }); it('lets an admin create a field definition and derives a unique key', function () { $admin = User::factory()->create(['role' => 'admin']); $this->actingAs($admin)->postJson('/api/custom-fields', [ 'label' => 'Beratungsschwerpunkt', 'type' => 'select', 'options' => ['Steuerrecht', 'Erbrecht'], ])->assertStatus(201)->assertJsonPath('data.key', 'beratungsschwerpunkt'); $this->assertDatabaseHas('custom_field_definitions', ['label' => 'Beratungsschwerpunkt']); }); it('appends new field definitions to the end of the list', function () { $admin = User::factory()->create(['role' => 'admin']); CustomFieldDefinition::factory()->create(['sort_order' => 2]); $this->actingAs($admin)->postJson('/api/custom-fields', [ 'label' => 'Zuletzt', 'type' => 'text', ])->assertStatus(201)->assertJsonPath('data.sort_order', 3); }); it('lets an admin reorder field definitions', function () { $admin = User::factory()->create(['role' => 'admin']); $a = CustomFieldDefinition::factory()->create(['sort_order' => 0]); $b = CustomFieldDefinition::factory()->create(['sort_order' => 1]); $c = CustomFieldDefinition::factory()->create(['sort_order' => 2]); $this->actingAs($admin)->postJson('/api/custom-fields/reorder', [ 'ids' => [$c->id, $a->id, $b->id], ])->assertStatus(200) ->assertJsonPath('data.0.id', $c->id) ->assertJsonPath('data.1.id', $a->id) ->assertJsonPath('data.2.id', $b->id); $this->assertDatabaseHas('custom_field_definitions', ['id' => $c->id, 'sort_order' => 0]); $this->assertDatabaseHas('custom_field_definitions', ['id' => $a->id, 'sort_order' => 1]); $this->assertDatabaseHas('custom_field_definitions', ['id' => $b->id, 'sort_order' => 2]); }); it('forbids staff from reordering field definitions', function () { $user = User::factory()->create(['role' => 'staff']); $definition = CustomFieldDefinition::factory()->create(); $this->actingAs($user)->postJson('/api/custom-fields/reorder', [ 'ids' => [$definition->id], ])->assertStatus(403); }); it('forbids staff from creating field definitions', function () { $user = User::factory()->create(['role' => 'staff']); $this->actingAs($user)->postJson('/api/custom-fields', [ 'label' => 'Heimlich', 'type' => 'text', ])->assertStatus(403); }); it('forbids staff from deleting field definitions', function () { $user = User::factory()->create(['role' => 'staff']); $definition = CustomFieldDefinition::factory()->create(); $this->actingAs($user)->deleteJson("/api/custom-fields/{$definition->id}")->assertStatus(403); }); it('requires options when the type is select', function () { $admin = User::factory()->create(['role' => 'admin']); $this->actingAs($admin)->postJson('/api/custom-fields', [ 'label' => 'Ohne Optionen', 'type' => 'select', ])->assertStatus(422); }); it('records a Verlauf entry when a field value changes', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $definition = CustomFieldDefinition::factory()->create(['label' => 'Beratungsschwerpunkt']); $this->actingAs($user)->putJson("/api/clients/{$client->id}/custom-fields/{$definition->id}", [ 'value' => 'Steuerrecht', ])->assertStatus(200); $this->assertDatabaseHas('custom_field_values', [ 'client_id' => $client->id, 'custom_field_definition_id' => $definition->id, 'value' => 'Steuerrecht', 'author_id' => $user->id, ]); $this->assertDatabaseHas('history_entries', [ 'client_id' => $client->id, 'type' => 'field_change', 'custom_field_definition_id' => $definition->id, 'author_id' => $user->id, ]); }); it('does not record a Verlauf entry when the value is unchanged', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $definition = CustomFieldDefinition::factory()->create(); $url = "/api/clients/{$client->id}/custom-fields/{$definition->id}"; $this->actingAs($user)->putJson($url, ['value' => 'Hallo'])->assertStatus(200); $this->actingAs($user)->putJson($url, ['value' => 'Hallo'])->assertStatus(200); expect(\App\Modules\History\Models\HistoryEntry::where('client_id', $client->id)->count())->toBe(1); }); it('validates select values against the defined options', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); $definition = CustomFieldDefinition::factory()->select(['Steuerrecht', 'Erbrecht'])->create(); $this->actingAs($user)->putJson("/api/clients/{$client->id}/custom-fields/{$definition->id}", [ 'value' => 'Strafrecht', ])->assertStatus(422); }); it('validates number values', function () { $admin = User::factory()->create(['role' => 'admin']); $client = Client::factory()->create(); $definition = CustomFieldDefinition::factory()->create(['type' => 'number']); $this->actingAs($admin)->putJson("/api/clients/{$client->id}/custom-fields/{$definition->id}", [ 'value' => 'not-a-number', ])->assertStatus(422); }); it('embeds custom fields on the client detail response', function () { $user = User::factory()->create(['role' => 'staff']); $client = Client::factory()->create(); CustomFieldDefinition::factory()->create(['label' => 'Mandant seit', 'sort_order' => 1]); $this->actingAs($user)->getJson("/api/clients/{$client->id}") ->assertStatus(200) ->assertJsonPath('data.custom_fields.0.label', 'Mandant seit') ->assertJsonPath('data.custom_fields.0.value', null); });