Support oft parent groups
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Domains\Admin\Actions\CreateParentGroup\CreateParentGroupAction;
|
||||
use App\Domains\Admin\Actions\CreateParentGroup\CreateParentGroupRequest;
|
||||
use App\Domains\Admin\Actions\DeleteParentGroup\DeleteParentGroupAction;
|
||||
use App\Domains\Admin\Actions\DeleteParentGroup\DeleteParentGroupRequest;
|
||||
use App\Domains\Admin\Actions\RenameParentGroup\RenameParentGroupAction;
|
||||
use App\Domains\Admin\Actions\RenameParentGroup\RenameParentGroupRequest;
|
||||
use App\Domains\Admin\Actions\UpdateGlobalSettings\UpdateGlobalSettingsAction;
|
||||
use App\Domains\Admin\Actions\UpdateGlobalSettings\UpdateGlobalSettingsRequest;
|
||||
use App\Domains\Admin\Repositories\AdminParentGroupRepository;
|
||||
use App\Domains\Event\Actions\CreateEvent\CreateEventCommand;
|
||||
use App\Domains\Event\Actions\CreateEvent\CreateEventRequest;
|
||||
use App\Domains\Event\Actions\UpdateEvent\UpdateEventCommand;
|
||||
use App\Domains\Event\Actions\UpdateEvent\UpdateEventRequest;
|
||||
use App\Enumerations\CostUnitType;
|
||||
use App\Enumerations\EatingHabit;
|
||||
use App\Enumerations\EfzStatus;
|
||||
use App\Enumerations\FirstAidPermission;
|
||||
use App\Enumerations\SwimmingPermission;
|
||||
use App\Enumerations\ParticipantGroupType;
|
||||
use App\Enumerations\ParticipationFeeType;
|
||||
use App\Models\Event;
|
||||
use App\Models\ParentGroup;
|
||||
use App\Models\Tenant;
|
||||
use App\Repositories\GlobalSettingRepository;
|
||||
use App\ValueObjects\Amount;
|
||||
use DateTime;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ParentGroupTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
private Tenant $tenant;
|
||||
private AdminParentGroupRepository $repository;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->tenant = $this->createTenant('tv', 'Test');
|
||||
app()->instance('tenant', $this->tenant);
|
||||
|
||||
DB::table('cost_unit_types')->insert(['slug' => CostUnitType::COST_UNIT_TYPE_EVENT, 'name' => 'Veranstaltung']);
|
||||
DB::table('participation_types')->insert(['slug' => 'participant', 'name' => 'Teilnehmer']);
|
||||
DB::table('participation_fee_types')->insert(['slug' => 'fixed', 'name' => 'Fix']);
|
||||
EatingHabit::create(['slug' => EatingHabit::EATING_HABIT_VEGAN, 'name' => 'Vegan']);
|
||||
EatingHabit::create(['slug' => EatingHabit::EATING_HABIT_VEGETARIAN, 'name' => 'Vegetarisch']);
|
||||
|
||||
$this->repository = new AdminParentGroupRepository();
|
||||
}
|
||||
|
||||
private function createTenant(string $slug, string $name, ?ParentGroup $parentGroup = null): Tenant
|
||||
{
|
||||
return Tenant::create([
|
||||
'slug' => $slug, 'name' => $name, 'email' => $slug . '@example.com',
|
||||
'email_finance' => $slug . '-f@example.com', 'url' => $slug . '.local',
|
||||
'account_name' => $name, 'account_iban' => 'DE00', 'account_bic' => 'XY',
|
||||
'city' => 'Stadt', 'postcode' => '00000', 'is_active_local_group' => true,
|
||||
'has_active_instance' => true, 'tax_liable' => false, 'vat_rate' => 0, 'vat_pricing_mode' => 'inclusive',
|
||||
'parent_group_id' => $parentGroup?->id,
|
||||
]);
|
||||
}
|
||||
|
||||
private function parentGroup(string $name): ParentGroup
|
||||
{
|
||||
return ParentGroup::where('name', $name)->firstOrFail();
|
||||
}
|
||||
|
||||
private function createEvent(ParticipantGroupType $type = ParticipantGroupType::LocalGroup): Event
|
||||
{
|
||||
$response = new CreateEventCommand(new CreateEventRequest(
|
||||
name: 'Sommerlager',
|
||||
location: 'Leipzig',
|
||||
postalCode: '04103',
|
||||
email: 'e@example.com',
|
||||
begin: new DateTime('2026-09-01'),
|
||||
end: new DateTime('2026-09-05'),
|
||||
earlyBirdEnd: new DateTime('2026-08-20'),
|
||||
registrationFinalEnd: new DateTime('2026-08-25'),
|
||||
earlyBirdEndAmountIncrease: 0,
|
||||
participationFeeType: ParticipationFeeType::where('slug', 'fixed')->first(),
|
||||
accountOwner: 'Owner',
|
||||
accountIban: 'DE00',
|
||||
payPerDay: false,
|
||||
participantGroupType: $type,
|
||||
))->execute();
|
||||
|
||||
$this->assertTrue($response->success);
|
||||
|
||||
return $response->event->fresh();
|
||||
}
|
||||
|
||||
private function updateGroupType(Event $event, ParticipantGroupType $type): Event
|
||||
{
|
||||
new UpdateEventCommand(new UpdateEventRequest(
|
||||
$event, $event->name, $event->location, $event->postal_code, $event->email,
|
||||
new DateTime('2026-08-20'), new DateTime('2026-08-25'), 16, true, true,
|
||||
Amount::fromString('0,00'), Amount::fromString('0,00'),
|
||||
$event->localGroups->pluck('id')->all(), [],
|
||||
participantGroupType: $type,
|
||||
))->execute();
|
||||
|
||||
return $event->fresh();
|
||||
}
|
||||
|
||||
// --- Vorbelegung und Tenant --------------------------------------------------------------------------
|
||||
|
||||
public function test_vorbelegung_ist_vorhanden(): void
|
||||
{
|
||||
$this->assertEqualsCanonicalizing(
|
||||
['Ohne Zuordnung', 'BdP', 'DPV', 'DPSG', 'VCP'],
|
||||
ParentGroup::pluck('name')->all(),
|
||||
);
|
||||
$this->assertSame('Ohne Zuordnung', ParentGroup::fallback()->name);
|
||||
}
|
||||
|
||||
public function test_neuer_tenant_ohne_angabe_landet_bei_ohne_zuordnung(): void
|
||||
{
|
||||
$this->assertSame(ParentGroup::fallback()->id, $this->tenant->fresh()->parent_group_id);
|
||||
}
|
||||
|
||||
// --- Verwaltung ----------------------------------------------------------------------------------------
|
||||
|
||||
public function test_anlegen_und_umbenennen(): void
|
||||
{
|
||||
$created = new CreateParentGroupAction(new CreateParentGroupRequest(' Pfadfinderbund '))->execute();
|
||||
$this->assertTrue($created->success);
|
||||
$this->assertSame('Pfadfinderbund', $created->parentGroup->name);
|
||||
|
||||
$renamed = new RenameParentGroupAction(new RenameParentGroupRequest($created->parentGroup, 'Neuer Name'))->execute();
|
||||
$this->assertTrue($renamed->success);
|
||||
$this->assertSame('Neuer Name', $created->parentGroup->fresh()->name);
|
||||
}
|
||||
|
||||
public function test_leerer_name_wird_abgelehnt(): void
|
||||
{
|
||||
$this->assertFalse(new CreateParentGroupAction(new CreateParentGroupRequest(' '))->execute()->success);
|
||||
$this->assertFalse(
|
||||
new RenameParentGroupAction(new RenameParentGroupRequest($this->parentGroup('BdP'), ''))->execute()->success
|
||||
);
|
||||
$this->assertSame('BdP', $this->parentGroup('BdP')->name);
|
||||
}
|
||||
|
||||
public function test_unbenutzte_gruppe_ist_loeschbar(): void
|
||||
{
|
||||
$dpv = $this->parentGroup('DPV');
|
||||
|
||||
$response = new DeleteParentGroupAction(new DeleteParentGroupRequest($dpv, $this->repository->isInUse($dpv)))->execute();
|
||||
|
||||
$this->assertTrue($response->success);
|
||||
$this->assertNull(ParentGroup::find($dpv->id));
|
||||
}
|
||||
|
||||
public function test_zugeordnete_gruppe_ist_nicht_loeschbar(): void
|
||||
{
|
||||
$bdp = $this->parentGroup('BdP');
|
||||
$this->createTenant('stamm-a', 'Stamm A', $bdp);
|
||||
|
||||
$this->assertTrue($this->repository->isInUse($bdp));
|
||||
|
||||
$response = new DeleteParentGroupAction(new DeleteParentGroupRequest($bdp, $this->repository->isInUse($bdp)))->execute();
|
||||
|
||||
$this->assertFalse($response->success);
|
||||
$this->assertNotNull(ParentGroup::find($bdp->id));
|
||||
}
|
||||
|
||||
public function test_fallback_ist_nie_loeschbar(): void
|
||||
{
|
||||
$fallback = ParentGroup::fallback();
|
||||
|
||||
$response = new DeleteParentGroupAction(new DeleteParentGroupRequest($fallback, false))->execute();
|
||||
|
||||
$this->assertFalse($response->success);
|
||||
$this->assertNotNull(ParentGroup::find($fallback->id));
|
||||
}
|
||||
|
||||
// --- Bezeichnungen -------------------------------------------------------------------------------------
|
||||
|
||||
public function test_bezeichnungen_haben_vorgaben(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
['localGroup' => 'Stamm', 'localGroupPlural' => 'Stämme', 'parentGroup' => 'Bund', 'parentGroupPlural' => 'Bünde'],
|
||||
ParticipantGroupType::labels(),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_geaenderte_bezeichnung_wirkt_auf_enum_und_event(): void
|
||||
{
|
||||
$response = new UpdateGlobalSettingsAction(new UpdateGlobalSettingsRequest(
|
||||
app(GlobalSettingRepository::class), 'Ortsgruppe', 'Ortsgruppen', 'Verband', 'Verbände',
|
||||
))->execute();
|
||||
|
||||
$this->assertTrue($response->success);
|
||||
$this->assertSame('Verband', ParticipantGroupType::ParentGroup->label());
|
||||
$this->assertSame('Ortsgruppen', ParticipantGroupType::LocalGroup->pluralLabel());
|
||||
}
|
||||
|
||||
public function test_leere_bezeichnung_wird_abgelehnt(): void
|
||||
{
|
||||
$response = new UpdateGlobalSettingsAction(new UpdateGlobalSettingsRequest(
|
||||
app(GlobalSettingRepository::class), 'Ortsgruppe', '', 'Verband', 'Verbände',
|
||||
))->execute();
|
||||
|
||||
$this->assertFalse($response->success);
|
||||
$this->assertSame('Stamm', ParticipantGroupType::LocalGroup->label());
|
||||
}
|
||||
|
||||
public function test_fehlende_einstellung_faellt_auf_vorgabe_zurueck(): void
|
||||
{
|
||||
DB::table('global_settings')->delete();
|
||||
|
||||
$this->assertSame('Bund', app(GlobalSettingRepository::class)->get('parent_group_label', 'Bund'));
|
||||
$this->assertSame('Stämme', ParticipantGroupType::LocalGroup->pluralLabel());
|
||||
}
|
||||
|
||||
// --- Event -------------------------------------------------------------------------------------------
|
||||
|
||||
public function test_anlage_speichert_abfrage_nach_bund(): void
|
||||
{
|
||||
$this->assertSame('parent_group', $this->createEvent(ParticipantGroupType::ParentGroup)->participant_group_type);
|
||||
$this->assertSame('local_group', $this->createEvent()->participant_group_type);
|
||||
}
|
||||
|
||||
public function test_optionen_sind_die_buende_der_teilnehmenden_staemme(): void
|
||||
{
|
||||
$event = $this->createEvent(ParticipantGroupType::ParentGroup);
|
||||
$event->localGroups()->sync([
|
||||
$this->createTenant('stamm-a', 'Stamm A', $this->parentGroup('VCP'))->id,
|
||||
$this->createTenant('stamm-b', 'Stamm B', $this->parentGroup('BdP'))->id,
|
||||
$this->createTenant('stamm-c', 'Stamm C', $this->parentGroup('BdP'))->id,
|
||||
]);
|
||||
|
||||
// Jeder Bund nur einmal, alphabetisch.
|
||||
$this->assertSame(['BdP', 'VCP'], $event->participantGroupOptions()->pluck('name')->all());
|
||||
}
|
||||
|
||||
public function test_optionen_listen_ohne_zuordnung_zuletzt(): void
|
||||
{
|
||||
$event = $this->createEvent(ParticipantGroupType::ParentGroup);
|
||||
$event->localGroups()->sync([
|
||||
$this->createTenant('stamm-a', 'Stamm A', $this->parentGroup('VCP'))->id,
|
||||
$this->createTenant('stamm-b', 'Stamm B', ParentGroup::fallback())->id,
|
||||
$this->createTenant('stamm-c', 'Stamm C', $this->parentGroup('BdP'))->id,
|
||||
]);
|
||||
|
||||
$this->assertSame(['BdP', 'VCP', 'Ohne Zuordnung'], $event->participantGroupOptions()->pluck('name')->all());
|
||||
}
|
||||
|
||||
public function test_verwaltung_listet_alphabetisch_mit_ohne_zuordnung_zuletzt(): void
|
||||
{
|
||||
new CreateParentGroupAction(new CreateParentGroupRequest('Pfadfinderbund'))->execute();
|
||||
|
||||
$this->assertSame(
|
||||
['BdP', 'DPSG', 'DPV', 'Pfadfinderbund', 'VCP', 'Ohne Zuordnung'],
|
||||
$this->repository->all()->pluck('name')->all(),
|
||||
);
|
||||
}
|
||||
|
||||
public function test_optionen_sind_bei_stamm_abfrage_die_staemme(): void
|
||||
{
|
||||
$event = $this->createEvent();
|
||||
$event->localGroups()->sync([$this->createTenant('stamm-a', 'Stamm A', $this->parentGroup('BdP'))->id]);
|
||||
|
||||
$this->assertSame(['Stamm A'], $event->participantGroupOptions()->pluck('name')->all());
|
||||
}
|
||||
|
||||
public function test_umstellen_ist_ohne_anmeldungen_moeglich(): void
|
||||
{
|
||||
$event = $this->updateGroupType($this->createEvent(), ParticipantGroupType::ParentGroup);
|
||||
|
||||
$this->assertSame('parent_group', $event->participant_group_type);
|
||||
}
|
||||
|
||||
public function test_umstellen_ist_nach_der_ersten_anmeldung_gesperrt(): void
|
||||
{
|
||||
$event = $this->createEvent();
|
||||
|
||||
// Eine Anmeldung genügt -- direkt geschrieben, der Anmeldeprozess ist hier nicht Gegenstand.
|
||||
EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_NOT_REQUIRED, 'name' => 'Nicht erforderlich']);
|
||||
SwimmingPermission::create(['slug' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED, 'name' => 'Darf baden', 'short' => 'Erteilt']);
|
||||
FirstAidPermission::create(['slug' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED, 'name' => 'Zugestimmt', 'description' => '']);
|
||||
|
||||
DB::table('event_participants')->insert([
|
||||
'efz_status' => EfzStatus::EFZ_STATUS_NOT_REQUIRED,
|
||||
'swimming_permission' => SwimmingPermission::SWIMMING_PERMISSION_ALLOWED,
|
||||
'first_aid_permission' => FirstAidPermission::FIRST_AID_PERMISSION_ALLOWED,
|
||||
'tenant' => $this->tenant->slug, 'event_id' => $event->id, 'identifier' => 'p-1',
|
||||
'firstname' => 'Hans', 'lastname' => 'Muster', 'participation_type' => 'participant',
|
||||
'birthday' => '2000-01-01', 'address_1' => 'Weg 1', 'postcode' => '04103', 'city' => 'Leipzig',
|
||||
'email_1' => 'h@example.com', 'phone_1' => '123', 'payment_purpose' => 'Beitrag',
|
||||
'eating_habit' => EatingHabit::EATING_HABIT_VEGAN,
|
||||
'arrival_date' => '2026-09-01', 'departure_date' => '2026-09-05',
|
||||
'arrival_eating' => 1, 'departure_eating' => 2, 'amount' => 0,
|
||||
'created_at' => now(), 'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$event = $this->updateGroupType($event, ParticipantGroupType::ParentGroup);
|
||||
|
||||
$this->assertSame('local_group', $event->participant_group_type);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user