wildeMoehre = $this->makeTenant('wm', 'Wilde Möhre'); 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']); ParticipationType::create(['slug' => ParticipationType::PARTICIPATION_TYPE_PARTICIPANT, 'name' => 'Teilnehmende']); PaymentMethod::create(['slug' => PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]); EfzStatus::create(['slug' => EfzStatus::EFZ_STATUS_NOT_CHECKED, 'name' => 'Nicht geprüft']); 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' => '']); app()->instance('tenant', $this->wildeMoehre); } /** * `create()` liefert nur die übergebenen Attribute zurück; die DB-Defaults (u.a. `tax_liable`) stehen * erst nach dem Neuladen bereit -- so, wie der Tenant im Betrieb aus der IdentifyTenant-Middleware kommt. */ private function makeTenant(string $slug, string $name): Tenant { return Tenant::create([ 'slug' => $slug, 'name' => $name, 'address_1' => 'Musterweg 1', 'email' => $slug . '@example.com', 'email_finance' => $slug . '-f@example.com', 'url' => $slug . '.test.local', 'account_name' => $name, 'account_iban' => 'DE00', 'account_bic' => 'XY', 'city' => 'Stadt', 'postcode' => '00000', 'invoice_prefix' => strtoupper($slug), 'is_active_local_group' => true, 'has_active_instance' => true, ])->fresh(); } private function createEvent(string $begin, string $name = 'Lager'): Event { $response = new CreateEventCommand(new CreateEventRequest( name: $name, location: 'Ort', postalCode: '00000', email: 'e@example.com', begin: new DateTime($begin), end: new DateTime($begin), earlyBirdEnd: new DateTime('2026-01-01'), registrationFinalEnd: new DateTime('2026-01-02'), earlyBirdEndAmountIncrease: 0, participationFeeType: ParticipationFeeType::where('slug', 'fixed')->first(), accountOwner: 'Owner', accountIban: 'DE00', payPerDay: false, ))->execute(); $this->assertTrue($response->success); return $response->event->fresh(); } /* |-------------------------------------------------------------------------- | Event-Teil der Nummer |-------------------------------------------------------------------------- */ public function test_events_of_the_same_month_are_numbered_consecutively(): void { $this->assertSame('WM-V-20260701', $this->createEvent('2026-07-16')->invoice_key); $this->assertSame('WM-V-20260702', $this->createEvent('2026-07-24')->invoice_key); // Anderer Monat, eigene Zählung. $this->assertSame('WM-V-20260801', $this->createEvent('2026-08-03')->invoice_key); } public function test_each_tenant_counts_for_itself(): void { $this->createEvent('2026-07-16'); app()->instance('tenant', $this->makeTenant('sn', 'Sachsen')); $this->assertSame('SN-V-20260701', $this->createEvent('2026-07-18')->invoice_key); } public function test_invoice_key_survives_moving_the_event_to_another_month(): void { $event = $this->createEvent('2026-07-16'); $event->update(['start_date' => '2026-11-05', 'end_date' => '2026-11-07']); // Eingefroren: eine bereits herausgegebene Rechnung gehört sonst plötzlich zu einer anderen Nummer. $this->assertSame('WM-V-20260701', $event->fresh()->invoice_key); } public function test_new_tenants_default_to_their_uppercased_slug(): void { $tenant = Tenant::create([ 'slug' => 'fen', 'name' => 'Fen', 'email' => 'fen@example.com', 'email_finance' => 'fen-f@example.com', 'url' => 'fen.test.local', 'account_name' => 'Fen', 'account_iban' => 'DE00', 'account_bic' => 'XY', 'city' => 'Stadt', 'postcode' => '00000', 'is_active_local_group' => true, 'has_active_instance' => true, ])->fresh(); // Ohne gesetztes Präfix greift der Migrations-Default: der großgeschriebene Slug. $this->assertSame('FEN', $tenant->invoice_prefix); app()->instance('tenant', $tenant); $this->assertSame('FEN-V-20260701', $this->createEvent('2026-07-16')->invoice_key); } /* |-------------------------------------------------------------------------- | Teilnehmer-Teil der Nummer |-------------------------------------------------------------------------- */ private function signUp(Event $event, string $firstname): void { $response = new SignUpCommand(new SignUpRequest( $event, null, $firstname, 'Muster', null, ParticipationType::PARTICIPATION_TYPE_PARTICIPANT, $this->wildeMoehre, new DateTime('2000-01-01'), 'Weg 1', null, '00000', 'Stadt', strtolower($firstname) . '@example.com', '123', null, null, null, null, null, null, null, 'vegan', null, null, false, false, false, false, false, new DateTime('2026-07-16'), new DateTime('2026-07-16'), 0, 0, null, new Amount(50.0, 'Euro'), PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION, ))->execute(); $this->assertTrue($response->success, $response->message ?? ''); } public function test_sequences_are_handed_out_in_order(): void { $event = $this->createEvent('2026-07-16'); $this->signUp($event, 'Ada'); $this->signUp($event, 'Bo'); $this->signUp($event, 'Cem'); $this->assertSame([1, 2, 3], $event->participants()->orderBy('id')->pluck('invoice_sequence')->all()); } public function test_signing_off_does_not_shift_later_sequences(): void { $event = $this->createEvent('2026-07-16'); $this->signUp($event, 'Ada'); $this->signUp($event, 'Bo'); // Abmeldung: der Datensatz bleibt bestehen, die Nummer damit vergeben. $event->participants()->orderBy('id')->first()->update(['unregistered_at' => '2026-07-01']); $this->signUp($event, 'Cem'); $this->assertSame(3, $event->participants()->orderBy('id')->get()->last()->invoice_sequence); } public function test_sequences_are_per_event(): void { $first = $this->createEvent('2026-07-16', 'Erstes'); $second = $this->createEvent('2026-07-24', 'Zweites'); $this->signUp($first, 'Ada'); $this->signUp($second, 'Bo'); $this->assertSame(1, $first->participants()->first()->invoice_sequence); $this->assertSame(1, $second->participants()->first()->invoice_sequence); } }