134 lines
5.2 KiB
PHP
134 lines
5.2 KiB
PHP
<?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. Nur einer trägt
|
|
// ein Einkaufsbeispiel -- der andere prüft den Fall einer ungepflegten Spalte.
|
|
DB::table('invoice_types')->insert([
|
|
[
|
|
'slug' => InvoiceType::INVOICE_TYPE_PROGRAM,
|
|
'name' => 'Programmkosten',
|
|
'purchase_example' => 'z. B. Bastelmaterial für den Workshop',
|
|
'sort_order' => 1,
|
|
'selectable' => true,
|
|
],
|
|
[
|
|
'slug' => InvoiceType::INVOICE_TYPE_OTHER,
|
|
'name' => 'Sonstige Kosten',
|
|
'purchase_example' => null,
|
|
'sort_order' => 3,
|
|
'selectable' => true,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function test_the_form_receives_the_purchase_example_of_each_type(): void
|
|
{
|
|
// Das Beispiel im Feld "Was wurde eingekauft" wird in der Tabelle gepflegt, nicht im Formular --
|
|
// sonst bräuchte jede Textänderung ein Deployment.
|
|
$response = $this->getJson('/api/v1/core/retrieve-invoice-types');
|
|
|
|
$response->assertOk();
|
|
$examples = array_column($response->json('invoiceTypes'), 'purchaseExample', 'slug');
|
|
|
|
$this->assertSame('z. B. Bastelmaterial für den Workshop', $examples[InvoiceType::INVOICE_TYPE_PROGRAM]);
|
|
|
|
// Ungepflegt: Das Formular setzt dann seinen eigenen allgemeinen Text ein.
|
|
$this->assertNull($examples[InvoiceType::INVOICE_TYPE_OTHER]);
|
|
}
|
|
|
|
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()
|
|
);
|
|
}
|
|
|
|
public function test_the_refund_type_does_not_count_as_an_expense(): void
|
|
{
|
|
// Sie mindert die Einnahmenseite bereits -- als Ausgabe gezählt, stünde sie zweimal in der Bilanz.
|
|
$this->assertSame(
|
|
[InvoiceType::INVOICE_TYPE_PROGRAM, InvoiceType::INVOICE_TYPE_OTHER],
|
|
InvoiceType::countingAsExpense()->pluck('slug')->all()
|
|
);
|
|
|
|
$type = InvoiceType::where('slug', InvoiceType::INVOICE_TYPE_PARTICIPATION_REFUND)->first();
|
|
$this->assertFalse($type->counts_as_expense);
|
|
}
|
|
}
|