tenant = Tenant::create([ 'slug' => 'tv', 'name' => 'Test', 'email' => 't@example.com', 'email_finance' => 'finance@example.com', 'url' => 'test.local', 'account_name' => 'Test e.V.', 'account_iban' => 'DE00', 'account_bic' => 'XY', 'city' => 'Stadt', 'postcode' => '00000', 'is_active_local_group' => true, 'has_active_instance' => true, ]); app()->instance('tenant', $this->tenant); 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']); } private function makeFee(float $amountStandard): EventParticipationFee { return EventParticipationFee::create([ 'tenant' => $this->tenant->slug, 'type' => 'participant', 'name' => 'Standard', 'description' => null, 'amount_standard' => $amountStandard, 'amount_reduced' => null, 'amount_solidarity' => null, ]); } private function makeEvent(bool $taxLiable, int $vatRate, string $mode, int $feeId): Event { return Event::create([ 'tenant' => $this->tenant->slug, 'name' => 'Event', 'identifier' => 'evt-' . uniqid(), 'location' => 'Ort', 'postal_code' => '00000', 'email' => 'e@example.com', 'start_date' => '2026-09-01', 'end_date' => '2026-09-05', 'early_bird_end' => '2026-08-20', 'registration_final_end' => '2026-08-25', 'early_bird_end_amount_increase' => 0, 'account_owner' => 'Owner', 'account_iban' => 'DE00', 'participation_fee_type' => 'fixed', 'participation_fee_1' => $feeId, 'pay_per_day' => false, 'pay_direct' => false, 'tax_liable' => $taxLiable, 'vat_rate' => $vatRate, 'vat_pricing_mode' => $mode, ]); } private function calculate(Event $event): float { return new EventResource($event)->calculateAmount( 'participant', 'standard', new DateTime('2026-09-01'), new DateTime('2026-09-01'), false, )->getAmount(); } public function test_add_on_mode_adds_vat_on_top(): void { $event = $this->makeEvent(taxLiable: true, vatRate: 19, mode: 'add_on', feeId: $this->makeFee(100.0)->id); $this->assertEqualsWithDelta(119.0, $this->calculate($event), 0.001); } public function test_inclusive_mode_keeps_amount(): void { $event = $this->makeEvent(taxLiable: true, vatRate: 19, mode: 'inclusive', feeId: $this->makeFee(100.0)->id); $this->assertEqualsWithDelta(100.0, $this->calculate($event), 0.001); } public function test_not_liable_ignores_add_on_mode(): void { $event = $this->makeEvent(taxLiable: false, vatRate: 19, mode: 'add_on', feeId: $this->makeFee(100.0)->id); $this->assertEqualsWithDelta(100.0, $this->calculate($event), 0.001); } public function test_create_event_snapshots_tenant_tax_config(): void { $this->tenant->update([ 'tax_liable' => true, 'vat_rate' => 7, 'vat_pricing_mode' => 'add_on', 'tax_exemption_reason' => null, 'tax_exemption_note' => null, ]); $response = new CreateEventCommand(new CreateEventRequest( name: 'Sommerlager', location: 'Ort', postalCode: '00000', 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, ))->execute(); $this->assertTrue($response->success); $event = $response->event->fresh(); $this->assertTrue($event->tax_liable); $this->assertSame(7, $event->vat_rate); $this->assertSame('add_on', $event->vat_pricing_mode); } }