Compare commits

...

11 Commits

Author SHA1 Message Date
philipp lang 1ba3d11325 Fix tests
continuous-integration/drone/push Build is passing Details
2025-11-12 03:30:38 +01:00
philipp lang 23183db56d Lint CreateExcelDocumentAction
continuous-integration/drone/push Build is failing Details
2025-11-12 02:24:57 +01:00
philipp lang 8d54ff131f Lint
continuous-integration/drone/push Build is failing Details
2025-11-12 02:19:48 +01:00
philipp lang 2f2c5c660b Fix stubs 2025-11-12 01:27:25 +01:00
philipp lang b5779c3ecf Fix tests
continuous-integration/drone/push Build is failing Details
2025-11-11 18:39:29 +01:00
philipp lang 1679b6bd0a Fix tests
continuous-integration/drone/push Build is failing Details
2025-11-11 10:28:38 +01:00
philipp lang 4f287c890b Fix tests
continuous-integration/drone/push Build is failing Details
2025-11-11 02:47:40 +01:00
philipp lang ae0aa30edf Fix participant scout index
continuous-integration/drone/push Build is failing Details
2025-11-11 01:51:18 +01:00
philipp lang 5917a8af6b Add ID to participant excel document
continuous-integration/drone/push Build is failing Details
2025-11-11 01:24:11 +01:00
philipp lang 3d067ecff9 Add cancelled participants to excel sheet 2025-11-11 01:16:53 +01:00
philipp lang 285662ba4f Add Soft deletes to participants
continuous-integration/drone/push Build is failing Details
2025-11-11 00:53:26 +01:00
12 changed files with 102 additions and 29 deletions

View File

@ -31,25 +31,27 @@ class CreateExcelDocumentAction
private function allSheet(Collection $participants): TableDocumentData private function allSheet(Collection $participants): TableDocumentData
{ {
$document = TableDocumentData::from(['title' => 'Anmeldungen für ' . $this->form->name, 'sheets' => []]); $document = TableDocumentData::from(['title' => 'Anmeldungen für ' . $this->form->name, 'sheets' => []]);
$headers = $this->form->getFields()->map(fn ($field) => $field->name)->toArray(); $headers = $this->form->getFields()->names()->push('Abgemeldet am')->prepend('ID')->toArray();
[$activeParticipants, $cancelledParticipants] = $participants->partition(fn ($participant) => $participant->cancelled_at === null);
$document->addSheet(SheetData::from([ $document->addSheet(SheetData::from([
'header' => $headers, 'header' => $headers,
'data' => $participants 'data' => $this->rowsFor($activeParticipants),
->map(fn ($participant) => $this->form->getFields()->map(fn ($field) => $participant->getFields()->find($field)->presentRaw())->toArray())
->toArray(),
'name' => 'Alle', 'name' => 'Alle',
])); ]));
$document->addSheet(SheetData::from([
'header' => $headers,
'data' => $this->rowsFor($cancelledParticipants),
'name' => 'Abgemeldet',
]));
if ($this->form->export->groupBy) { if ($this->form->export->groupBy) {
$groups = $participants->groupBy(fn ($participant) => $participant->getFields()->findByKey($this->form->export->groupBy)->presentRaw()); $groups = $activeParticipants->groupBy(fn ($participant) => $participant->getFields()->findByKey($this->form->export->groupBy)->presentRaw());
foreach ($groups as $name => $participants) { foreach ($groups as $name => $groupedParticipants) {
$document->addSheet(SheetData::from([ $document->addSheet(SheetData::from([
'header' => $headers, 'header' => $headers,
'data' => $participants 'data' => $this->rowsFor($groupedParticipants),
->map(fn ($participant) => $this->form->getFields()->map(fn ($field) => $participant->getFields()->find($field)->presentRaw())->toArray())
->toArray(),
'name' => $name, 'name' => $name,
])); ]));
} }
@ -64,6 +66,17 @@ class CreateExcelDocumentAction
return $document; return $document;
} }
/**
* @param Collection<int, Participant> $participants
* @return array<int, array<string, mixed>>
*/
public function rowsFor(Collection $participants): array {
return $participants->map(fn ($participant) => $participant->getFields()->presentValues()
->put('Abgemeldet am', $participant->cancelled_at?->format('d.m.Y H:i:s') ?: '')
->prepend((string) $participant->id, 'ID')
)->toArray();
}
private function tempPath(): string private function tempPath(): string
{ {
return sys_get_temp_dir() . '/' . str()->uuid()->toString(); return sys_get_temp_dir() . '/' . str()->uuid()->toString();

View File

@ -15,7 +15,7 @@ class ParticipantDestroyAction
public function handle(int $participantId): void public function handle(int $participantId): void
{ {
Participant::findOrFail($participantId)->delete(); Participant::findOrFail($participantId)->update(['cancelled_at' => now()]);
} }
public function asController(Participant $participant): void public function asController(Participant $participant): void

View File

@ -18,10 +18,10 @@ class UpdateParticipantSearchIndexAction
$form->searchableUsing()->updateIndexSettings( $form->searchableUsing()->updateIndexSettings(
$form->participantsSearchableAs(), $form->participantsSearchableAs(),
[ [
'filterableAttributes' => [...$form->getFields()->filterables()->getKeys(), 'parent-id'], 'filterableAttributes' => [...$form->getFields()->filterables()->getKeys(), 'parent-id', 'cancelled_at'],
'searchableAttributes' => $form->getFields()->searchables()->getKeys(), 'searchableAttributes' => $form->getFields()->searchables()->getKeys(),
'sortableAttributes' => [...$form->getFields()->sortables()->getKeys(), 'id', 'created_at'], 'sortableAttributes' => [...$form->getFields()->sortables()->getKeys(), 'id', 'created_at'],
'displayedAttributes' => [...$form->getFields()->filterables()->getKeys(), ...$form->getFields()->searchables()->getKeys(), 'id'], 'displayedAttributes' => [...$form->getFields()->filterables()->getKeys(), ...$form->getFields()->searchables()->getKeys(), 'id', 'cancelled_at'],
'pagination' => [ 'pagination' => [
'maxTotalHits' => 1000000, 'maxTotalHits' => 1000000,
] ]

View File

@ -99,19 +99,19 @@ class FieldCollection extends Collection
} }
/** /**
* @return array<int, string> * @return Collection<int, string>
*/ */
public function names(): array public function names(): Collection
{ {
return $this->map(fn ($field) => $field->name)->toArray(); return $this->map(fn ($field) => $field->name);
} }
/** /**
* @return array<int, string> * @return Collection<string, string>
*/ */
public function presentValues(): array public function presentValues(): Collection
{ {
return $this->map(fn ($field) => $field->presentRaw())->toArray(); return $this->mapWithKeys(fn ($field) => [$field->name => $field->presentRaw()]);
} }
public function hasSpecialType(SpecialType $specialType): bool public function hasSpecialType(SpecialType $specialType): bool

View File

@ -31,6 +31,7 @@ class Participant extends Model implements Preventable
public $casts = [ public $casts = [
'data' => 'json', 'data' => 'json',
'last_remembered_at' => 'datetime', 'last_remembered_at' => 'datetime',
'cancelled_at' => 'datetime',
]; ];
/** /**
@ -108,7 +109,12 @@ class Participant extends Model implements Preventable
/** @return array<string, mixed> */ /** @return array<string, mixed> */
public function toSearchableArray(): array public function toSearchableArray(): array
{ {
return [...$this->data, 'parent-id' => $this->parent_id, 'created_at' => $this->created_at->timestamp]; return [
...$this->data,
'parent-id' => $this->parent_id,
'created_at' => $this->created_at->timestamp,
'cancelled_at' => $this->cancelled_at
];
} }
public function matchesCondition(Condition $condition): bool { public function matchesCondition(Condition $condition): bool {

View File

@ -54,6 +54,8 @@ class ParticipantFilterScope extends ScoutFilter
$filter->push('parent-id IS NULL'); $filter->push('parent-id IS NULL');
} }
$filter->push('cancelled_at IS NULL');
if ($this->parent !== null && $this->parent !== -1) { if ($this->parent !== null && $this->parent !== -1) {
$filter->push('parent-id = ' . $this->parent); $filter->push('parent-id = ' . $this->parent);
} }

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('participants', function (Blueprint $table) {
$table->datetime('cancelled_at')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('participants', function (Blueprint $table) {
$table->dropColumn('cancelled_at');
});
}
};

@ -1 +1 @@
Subproject commit 7304963370ff64fb5accf08da4864981cc424301 Subproject commit f7b04591830ebdeaddf76236e4cbc87a8b3eec8f

View File

@ -269,6 +269,15 @@ it('testItShowsPreventionState', function () {
->assertJsonPath('data.0.prevention_items.0.tooltip', 'erweitertes Führungszeugnis nicht vorhanden'); ->assertJsonPath('data.0.prevention_items.0.tooltip', 'erweitertes Führungszeugnis nicht vorhanden');
}); });
it('doesnt show cancelled participants', function () {
$this->login()->loginNami()->withoutExceptionHandling();
$participant = Participant::factory()->for(Form::factory())->create(['cancelled_at' => now()]);
sleep(2);
$this->callFilter('form.participant.index', [], ['form' => $participant->form])
->assertJsonCount(0, 'data');
});
it('test it orders participants by value', function (array $values, array $sorting, array $expected) { it('test it orders participants by value', function (array $values, array $sorting, array $expected) {
list($key, $direction) = $sorting; list($key, $direction) = $sorting;
$this->login()->loginNami()->withoutExceptionHandling(); $this->login()->loginNami()->withoutExceptionHandling();

View File

@ -24,5 +24,9 @@ it('testItCanDestroyAParticipant', function () {
$this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()])) $this->deleteJson(route('participant.destroy', ['participant' => $form->participants->first()]))
->assertOk(); ->assertOk();
$this->assertDatabaseCount('participants', 0); $this->assertDatabaseCount('participants', 1);
$this->assertDatabaseHas('participants', [
'cancelled_at' => now(),
'id' => $form->participants->first()->id,
]);
}); });

View File

@ -2,6 +2,7 @@
namespace Tests\Feature\Form; namespace Tests\Feature\Form;
use DB;
use App\Form\Models\Form; use App\Form\Models\Form;
use App\Form\Models\Participant; use App\Form\Models\Participant;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
@ -20,15 +21,14 @@ it('testItShowsParticipantsAndColumns', function () {
$this->login()->loginNami()->withoutExceptionHandling(); $this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory() $form = Form::factory()
->has(Participant::factory()->data(['stufe' => 'Pfadfinder', 'vorname' => 'Max', 'select' => ['A', 'B']])) ->has(Participant::factory()->data(['stufe' => 'Pfadfinder', 'vorname' => 'Max', 'select' => ['A', 'B']]))
->sections([ ->fields([
FormtemplateSectionRequest::new()->fields([
$this->textField('vorname')->name('Vorname'), $this->textField('vorname')->name('Vorname'),
$this->checkboxesField('select')->name('Abcselect')->options(['A', 'B', 'C']), $this->checkboxesField('select')->name('Abcselect')->options(['A', 'B', 'C']),
$this->dropdownField('stufe')->name('Stufe')->options(['Wölfling', 'Jungpfadfinder', 'Pfadfinder']), $this->dropdownField('stufe')->name('Stufe')->options(['Wölfling', 'Jungpfadfinder', 'Pfadfinder']),
]),
]) ])
->name('ZEM 2024') ->name('ZEM 2024')
->create(); ->create();
DB::table('participants')->where('id', $form->participants->first()->id)->update(['id' => 9909]);
$this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx'); $this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx');
$contents = Storage::disk('temp')->get('tn-zem-2024.xlsx'); $contents = Storage::disk('temp')->get('tn-zem-2024.xlsx');
@ -37,4 +37,17 @@ it('testItShowsParticipantsAndColumns', function () {
$this->assertExcelContent('Pfadfinder', $contents); $this->assertExcelContent('Pfadfinder', $contents);
$this->assertExcelContent('Stufe', $contents); $this->assertExcelContent('Stufe', $contents);
$this->assertExcelContent('Abcselect', $contents); $this->assertExcelContent('Abcselect', $contents);
$this->assertExcelContent('9909', $contents);
});
it('shows cancelled at', function () {
Storage::fake('temp');
$this->login()->loginNami()->withoutExceptionHandling();
$form = Form::factory()->name('ZEM 2024')
->has(Participant::factory()->state(['cancelled_at' => now()->subWeek()]))
->create();
$this->get(route('form.export', ['form' => $form]))->assertDownload('tn-zem-2024.xlsx');
$contents = Storage::disk('temp')->get('tn-zem-2024.xlsx');
$this->assertExcelContent(now()->subWeek()->format('d.m.Y'), $contents);
}); });

View File

@ -1,7 +1,5 @@
<?php <?php
namespace Illuminate\Testing;
namespace Spatie\LaravelSettings; namespace Spatie\LaravelSettings;
/** /**