From 8d6a4041c1faccab13d7cc872773460aaad873f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thomas=20G=C3=BCnther?=
Date: Wed, 29 Jul 2026 13:10:35 +0200
Subject: [PATCH] Configurations for payment modules
---
.../PaymentMethodConfigurationTest.php | 181 ++++++++++++++++++
tests/Unit/PaymentMethodOptionsTest.php | 59 ++++++
2 files changed, 240 insertions(+)
create mode 100644 tests/Feature/PaymentMethodConfigurationTest.php
create mode 100644 tests/Unit/PaymentMethodOptionsTest.php
diff --git a/tests/Feature/PaymentMethodConfigurationTest.php b/tests/Feature/PaymentMethodConfigurationTest.php
new file mode 100644
index 0000000..c7127a4
--- /dev/null
+++ b/tests/Feature/PaymentMethodConfigurationTest.php
@@ -0,0 +1,181 @@
+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);
+
+ PaymentMethod::create(['slug' => PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);
+ PaymentMethod::create(['slug' => PaymentMethod::PAYMENT_NOT_DEFINED]);
+
+ DB::table('participation_fee_types')->insert([
+ 'slug' => 'FIXED',
+ 'name' => 'Fixed',
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ]);
+ }
+
+ private function availableMethod(string $slug, bool $active = false, array $config = []): AvailablePaymentMethod
+ {
+ return AvailablePaymentMethod::create([
+ 'tenant' => $this->tenant->slug,
+ 'slug' => $slug,
+ 'name' => 'Konto',
+ 'description' => null,
+ 'active' => $active,
+ 'configuration' => $config,
+ ]);
+ }
+
+ public function test_activation_is_blocked_when_required_options_missing(): void
+ {
+ $method = $this->availableMethod(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION);
+
+ $response = new UpdateAvailablePaymentMethodAction(new UpdateAvailablePaymentMethodRequest(
+ availablePaymentMethod: $method,
+ name: 'Konto',
+ description: null,
+ active: true,
+ configuration: ['iban' => 'DE123'], // account_owner fehlt
+ ))->execute();
+
+ $this->assertFalse($response->success);
+ $this->assertFalse($method->fresh()->active);
+ }
+
+ public function test_activation_succeeds_when_configuration_complete(): void
+ {
+ $method = $this->availableMethod(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION);
+
+ $response = new UpdateAvailablePaymentMethodAction(new UpdateAvailablePaymentMethodRequest(
+ availablePaymentMethod: $method,
+ name: 'Konto',
+ description: null,
+ active: true,
+ configuration: ['iban' => 'DE123', 'account_owner' => 'Kasse', 'bic' => 'XY', 'evil' => 'x'],
+ ))->execute();
+
+ $this->assertTrue($response->success);
+
+ $fresh = $method->fresh();
+ $this->assertTrue($fresh->active);
+ $this->assertSame('DE123', $fresh->configuration['iban']);
+ $this->assertArrayNotHasKey('evil', $fresh->configuration); // unbekannte Keys verworfen
+ }
+
+ public function test_event_assignment_copies_snapshot_and_preserves_overrides(): void
+ {
+ $this->availableMethod(
+ PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION,
+ active: true,
+ config: ['iban' => 'DE-TENANT', 'account_owner' => 'Kasse'],
+ );
+
+ $event = $this->createEvent();
+
+ // 1. Zuweisung -> Snapshot der Tenant-Config landet im Pivot.
+ $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);
+
+ $pivotConfig = $event->fresh()->paymentMethods()->first()->pivot->configuration;
+ $this->assertSame('DE-TENANT', $pivotConfig['iban']);
+
+ // 2. Pro-Event-Override setzen.
+ $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION], [
+ PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION => ['iban' => 'DE-EVENT', 'account_owner' => 'Kasse'],
+ ]);
+ $this->assertSame('DE-EVENT', $event->fresh()->paymentMethods()->first()->pivot->configuration['iban']);
+
+ // 3. Erneutes Speichern OHNE Override -> Override bleibt erhalten (kein Reset-Wipe).
+ $this->runUpdateEvent($event, [PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION]);
+ $this->assertSame('DE-EVENT', $event->fresh()->paymentMethods()->first()->pivot->configuration['iban']);
+ }
+
+ private function createEvent(): Event
+ {
+ return Event::create([
+ 'tenant' => $this->tenant->slug,
+ 'name' => 'Event',
+ 'identifier' => 'evt-1',
+ 'location' => 'Ort',
+ 'postal_code' => '00000',
+ 'email' => 'e@example.com',
+ 'start_date' => '2026-08-01',
+ 'end_date' => '2026-08-05',
+ 'early_bird_end' => '2026-07-01',
+ 'registration_final_end' => '2026-07-20',
+ 'early_bird_end_amount_increase' => 0,
+ 'account_owner' => 'Owner',
+ 'account_iban' => 'DE00',
+ 'participation_fee_type' => 'FIXED',
+ 'pay_per_day' => false,
+ 'pay_direct' => false,
+ ]);
+ }
+
+ /**
+ * @param array $slugs
+ * @param array> $configs
+ */
+ private function runUpdateEvent(Event $event, array $slugs, array $configs = []): void
+ {
+ $request = new UpdateEventRequest(
+ $event,
+ $event->name,
+ $event->location,
+ $event->postal_code,
+ $event->email,
+ new \DateTime('2026-07-01'),
+ new \DateTime('2026-07-20'),
+ 16,
+ false,
+ false,
+ Amount::fromString('0'),
+ Amount::fromString('0'),
+ [],
+ [],
+ $slugs,
+ $configs,
+ );
+
+ $response = new UpdateEventCommand($request)->execute();
+ $this->assertTrue($response->success, $response->message);
+ }
+}
diff --git a/tests/Unit/PaymentMethodOptionsTest.php b/tests/Unit/PaymentMethodOptionsTest.php
new file mode 100644
index 0000000..5c95f00
--- /dev/null
+++ b/tests/Unit/PaymentMethodOptionsTest.php
@@ -0,0 +1,59 @@
+assertNotEmpty($options);
+ $this->assertSame('account_owner', $options[0]['name']);
+ }
+
+ public function test_options_for_returns_empty_for_unknown_slug(): void
+ {
+ $this->assertSame([], PaymentMethod::optionsFor('DOES_NOT_EXIST'));
+ }
+
+ public function test_required_option_keys_only_returns_required(): void
+ {
+ $keys = PaymentMethod::requiredOptionKeys(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION);
+
+ $this->assertContains('account_owner', $keys);
+ $this->assertContains('iban', $keys);
+ $this->assertNotContains('bic', $keys); // bic ist optional
+ }
+
+ public function test_sanitize_configuration_drops_unknown_keys(): void
+ {
+ $sanitized = PaymentMethod::sanitizeConfiguration(PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION, [
+ 'iban' => 'DE123',
+ 'account_owner' => 'Kasse',
+ 'evil' => 'x',
+ ]);
+
+ $this->assertArrayHasKey('iban', $sanitized);
+ $this->assertArrayHasKey('account_owner', $sanitized);
+ $this->assertArrayNotHasKey('evil', $sanitized);
+ }
+
+ public function test_is_configuration_complete_requires_all_required_options(): void
+ {
+ $slug = PaymentMethod::PAYMENT_ACCOUNT_TRANSACTION;
+
+ $this->assertFalse(PaymentMethod::isConfigurationComplete($slug, []));
+ $this->assertFalse(PaymentMethod::isConfigurationComplete($slug, ['iban' => 'DE123']));
+ $this->assertFalse(PaymentMethod::isConfigurationComplete($slug, ['iban' => 'DE123', 'account_owner' => '']));
+ $this->assertTrue(PaymentMethod::isConfigurationComplete($slug, ['iban' => 'DE123', 'account_owner' => 'Kasse']));
+ }
+
+ public function test_slug_without_options_is_always_complete(): void
+ {
+ $this->assertTrue(PaymentMethod::isConfigurationComplete(PaymentMethod::PAYMENT_NOT_DEFINED, []));
+ }
+}