Approven von Beitragserstattungen

This commit is contained in:
2026-09-03 23:08:41 +02:00
parent b01288b5e0
commit efee20c16b
15 changed files with 994 additions and 39 deletions
@@ -0,0 +1,93 @@
<?php
namespace Tests\Feature;
use App\Enumerations\InvoiceType;
use App\Models\Tenant;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;
/**
* Welche Auslagentypen zur Auswahl stehen.
*
* Die Beitragserstattung entsteht ausschließlich aus einem bestätigten Erstattungsvorgang, der die
* Bankverbindung, den Beleg und den Bezug zur Anmeldung mitbringt. Von Hand gewählt stünde ein leerer
* Rahmen ohne diesen Vorgang da -- deshalb darf sie in keinem Formular auftauchen.
*/
class InvoiceTypeSelectionTest extends TestCase
{
use RefreshDatabase;
protected function setUp(): void
{
parent::setUp();
$tenant = Tenant::create([
'slug' => 'wm',
'name' => 'Wilde Möhre',
'email' => 'wm@example.com',
'email_finance' => 'wm-f@example.com',
'url' => parse_url(config('app.url'), PHP_URL_HOST),
'account_name' => 'Wilde Möhre e.V.',
'account_iban' => 'DE00',
'account_bic' => 'XY',
'city' => 'Stadt',
'postcode' => '00000',
'is_active_local_group' => true,
'has_active_instance' => true,
]);
app()->instance('tenant', $tenant);
// Zwei wählbare Typen neben der Beitragserstattung, die aus der Migration kommt.
DB::table('invoice_types')->insert([
['slug' => InvoiceType::INVOICE_TYPE_PROGRAM, 'name' => 'Programmkosten', 'sort_order' => 1, 'selectable' => true],
['slug' => InvoiceType::INVOICE_TYPE_OTHER, 'name' => 'Sonstige Kosten', 'sort_order' => 3, 'selectable' => true],
]);
}
public function test_the_refund_type_exists_but_is_not_selectable(): void
{
// `where` und nicht `find`: InvoiceType deklariert keinen Primärschlüssel, `find` suchte nach `id`.
$type = InvoiceType::where('slug', InvoiceType::INVOICE_TYPE_PARTICIPATION_REFUND)->first();
$this->assertNotNull($type, 'Der Typ muss existieren -- invoices.type ist ein Fremdschlüssel darauf.');
$this->assertFalse($type->selectable);
}
public function test_it_is_missing_from_the_new_invoice_form(): void
{
$response = $this->getJson('/api/v1/core/retrieve-invoice-types');
$response->assertOk();
$slugs = array_column($response->json('invoiceTypes'), 'slug');
$this->assertNotContains(InvoiceType::INVOICE_TYPE_PARTICIPATION_REFUND, $slugs);
$this->assertContains(InvoiceType::INVOICE_TYPE_PROGRAM, $slugs);
$this->assertContains(InvoiceType::INVOICE_TYPE_OTHER, $slugs);
}
public function test_it_is_missing_when_a_treasurer_rebooks(): void
{
// Ohne diese Sperre ließe sich jede beliebige Abrechnung nachträglich zu einer
// Beitragserstattung machen, ohne dass ein Erstattungsvorgang dahinterstünde.
$response = $this->getJson('/api/v1/core/retrieve-invoice-types-all');
$response->assertOk();
$slugs = array_column($response->json('invoiceTypes'), 'slug');
$this->assertNotContains(InvoiceType::INVOICE_TYPE_PARTICIPATION_REFUND, $slugs);
$this->assertContains(InvoiceType::INVOICE_TYPE_PROGRAM, $slugs);
// "Sonstige Kosten" steht dort bewusst am Ende.
$this->assertSame(InvoiceType::INVOICE_TYPE_OTHER, end($slugs));
}
public function test_selectable_returns_only_choosable_types_in_order(): void
{
$this->assertSame(
[InvoiceType::INVOICE_TYPE_PROGRAM, InvoiceType::INVOICE_TYPE_OTHER],
InvoiceType::selectable()->pluck('slug')->all()
);
}
}