Add InvoiceIndexAction
This commit is contained in:
parent
15b62e59fc
commit
27cbf8bcd9
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Invoice\Actions;
|
||||||
|
|
||||||
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
use App\Invoice\Models\Invoice;
|
||||||
|
use App\Invoice\Resources\InvoiceResource;
|
||||||
|
use Illuminate\Pagination\LengthAwarePaginator;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Inertia\Response;
|
||||||
|
|
||||||
|
class InvoiceIndexAction
|
||||||
|
{
|
||||||
|
use AsAction;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return LengthAwarePaginator<Invoice>
|
||||||
|
*/
|
||||||
|
public function handle(): LengthAwarePaginator
|
||||||
|
{
|
||||||
|
return Invoice::select('*')->with('positions')->paginate(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function asController(): Response
|
||||||
|
{
|
||||||
|
return Inertia::render('invoice/Index', [
|
||||||
|
'data' => InvoiceResource::collection($this->handle()),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,11 @@ class Invoice extends Model
|
||||||
|
|
||||||
public $casts = [
|
public $casts = [
|
||||||
'to' => 'json',
|
'to' => 'json',
|
||||||
|
'status' => InvoiceStatus::class,
|
||||||
|
];
|
||||||
|
|
||||||
|
public $dates = [
|
||||||
|
'sent_at',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Invoice\Resources;
|
||||||
|
|
||||||
|
use App\Invoice\Models\Invoice;
|
||||||
|
use App\Lib\HasMeta;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mixin Invoice
|
||||||
|
*/
|
||||||
|
class InvoiceResource extends JsonResource
|
||||||
|
{
|
||||||
|
|
||||||
|
use HasMeta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform the resource into an array.
|
||||||
|
*
|
||||||
|
* @param \Illuminate\Http\Request $request
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function toArray($request)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'to_name' => $this->to['name'],
|
||||||
|
'sum_human' => number_format($this->positions->sum('price') / 100, 2, ',', '') . ' €',
|
||||||
|
'sent_at_human' => $this->sent_at->format('d.m.Y'),
|
||||||
|
'status' => $this->status->value,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public static function meta(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'links' => [
|
||||||
|
'mass-store' => route('invoice.mass-store'),
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -157,7 +157,6 @@ class MemberResource extends JsonResource
|
||||||
'links' => [
|
'links' => [
|
||||||
'index' => route('member.index'),
|
'index' => route('member.index'),
|
||||||
'create' => route('member.create'),
|
'create' => route('member.create'),
|
||||||
'allpayment' => route('allpayment.page'),
|
|
||||||
'sendpayment' => route('sendpayment.create'),
|
'sendpayment' => route('sendpayment.create'),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Payment\Actions;
|
|
||||||
|
|
||||||
use Inertia;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class AllpaymentPageAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
public function handle(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(): Response
|
|
||||||
{
|
|
||||||
session()->put('menu', 'member');
|
|
||||||
session()->put('title', 'Rechnungen erstellen');
|
|
||||||
|
|
||||||
return Inertia::render('allpayment/VForm', $this->handle());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories\Invoice\Models;
|
||||||
|
|
||||||
|
use App\Invoice\Enums\InvoiceStatus;
|
||||||
|
use App\Invoice\Models\Invoice;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Tests\Feature\Invoice\ReceiverRequestFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<Invoice>
|
||||||
|
*/
|
||||||
|
class InvoiceFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = Invoice::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'greeting' => $this->faker->words(4, true),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function to(ReceiverRequestFactory $to): self
|
||||||
|
{
|
||||||
|
return $this->state(['to' => $to->create()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function sentAt(Carbon $sentAt): self
|
||||||
|
{
|
||||||
|
return $this->state(['sent_at' => $sentAt]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function status(InvoiceStatus $status): self
|
||||||
|
{
|
||||||
|
return $this->state(['status' => $status->value]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories\Invoice\Models;
|
||||||
|
|
||||||
|
use App\Invoice\Models\InvoicePosition;
|
||||||
|
use App\Member\Member;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Factory<InvoicePosition>
|
||||||
|
*/
|
||||||
|
class InvoicePositionFactory extends Factory
|
||||||
|
{
|
||||||
|
protected $model = InvoicePosition::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'description' => $this->faker->words(4, true),
|
||||||
|
'member_id' => Member::factory()->defaults()->create()->id,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function price(int $price): self
|
||||||
|
{
|
||||||
|
return $this->state(['price' => $price]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ return new class extends Migration
|
||||||
$table->json('to');
|
$table->json('to');
|
||||||
$table->string('greeting');
|
$table->string('greeting');
|
||||||
$table->string('status');
|
$table->string('status');
|
||||||
|
$table->date('sent_at')->nullable();
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
<template>
|
|
||||||
<page-layout>
|
|
||||||
<form class="p-6 grid gap-4 justify-start" @submit.prevent="submit">
|
|
||||||
<f-text id="year" v-model="inner.year" label="Jahr" required></f-text>
|
|
||||||
|
|
||||||
<f-switch id="for_promise" label="Versprechen einbeziehen" v-model="inner.for_promise" size="sm"></f-switch>
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Absenden</button>
|
|
||||||
</form>
|
|
||||||
</page-layout>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
inner: {
|
|
||||||
for_promise: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
submit() {
|
|
||||||
this.$inertia.post(`/allpayment`, this.inner);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -25,6 +25,7 @@ use App\Initialize\Actions\InitializeFormAction;
|
||||||
use App\Initialize\Actions\NamiGetSearchLayerAction;
|
use App\Initialize\Actions\NamiGetSearchLayerAction;
|
||||||
use App\Initialize\Actions\NamiLoginCheckAction;
|
use App\Initialize\Actions\NamiLoginCheckAction;
|
||||||
use App\Initialize\Actions\NamiSearchAction;
|
use App\Initialize\Actions\NamiSearchAction;
|
||||||
|
use App\Invoice\Actions\InvoiceIndexAction;
|
||||||
use App\Invoice\Actions\MassStoreAction;
|
use App\Invoice\Actions\MassStoreAction;
|
||||||
use App\Maildispatcher\Actions\CreateAction;
|
use App\Maildispatcher\Actions\CreateAction;
|
||||||
use App\Maildispatcher\Actions\DestroyAction;
|
use App\Maildispatcher\Actions\DestroyAction;
|
||||||
|
@ -46,8 +47,6 @@ use App\Membership\Actions\MembershipDestroyAction;
|
||||||
use App\Membership\Actions\MembershipStoreAction;
|
use App\Membership\Actions\MembershipStoreAction;
|
||||||
use App\Membership\Actions\MembershipUpdateAction;
|
use App\Membership\Actions\MembershipUpdateAction;
|
||||||
use App\Membership\Actions\StoreForGroupAction;
|
use App\Membership\Actions\StoreForGroupAction;
|
||||||
use App\Payment\Actions\AllpaymentPageAction;
|
|
||||||
use App\Payment\Actions\AllpaymentStoreAction;
|
|
||||||
use App\Payment\Actions\DisplayPdfAction;
|
use App\Payment\Actions\DisplayPdfAction;
|
||||||
use App\Payment\Actions\IndexAction as PaymentIndexAction;
|
use App\Payment\Actions\IndexAction as PaymentIndexAction;
|
||||||
use App\Payment\Actions\PaymentDestroyAction;
|
use App\Payment\Actions\PaymentDestroyAction;
|
||||||
|
@ -113,10 +112,10 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
||||||
Route::delete('/payment/{payment}', PaymentDestroyAction::class)->name('payment.destroy');
|
Route::delete('/payment/{payment}', PaymentDestroyAction::class)->name('payment.destroy');
|
||||||
|
|
||||||
// -------------------------------- allpayment ---------------------------------
|
// -------------------------------- allpayment ---------------------------------
|
||||||
Route::get('allpayment', AllpaymentPageAction::class)->name('allpayment.page');
|
Route::post('/invoice/mass-store', MassStoreAction::class)->name('invoice.mass-store');
|
||||||
Route::post('allpayment', MassStoreAction::class)->name('allpayment.store');
|
|
||||||
|
|
||||||
// ---------------------------------- invoice ----------------------------------
|
// ---------------------------------- invoice ----------------------------------
|
||||||
|
Route::get('/invoice', InvoiceIndexAction::class)->name('invoice.index');
|
||||||
Route::post('/invoice', InvoiceStoreAction::class)->name('invoice.store');
|
Route::post('/invoice', InvoiceStoreAction::class)->name('invoice.store');
|
||||||
|
|
||||||
// --------------------------------- membership --------------------------------
|
// --------------------------------- membership --------------------------------
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Invoice;
|
||||||
|
|
||||||
|
use App\Invoice\Enums\InvoiceStatus;
|
||||||
|
use App\Invoice\Models\Invoice;
|
||||||
|
use App\Invoice\Models\InvoicePosition;
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class InvoiceIndexActionTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function testItDisplaysInvoices(): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
Invoice::factory()
|
||||||
|
->has(InvoicePosition::factory()->price(1100), 'positions')
|
||||||
|
->has(InvoicePosition::factory()->price(2200), 'positions')
|
||||||
|
->to(ReceiverRequestFactory::new()->name('Familie Blabla'))
|
||||||
|
->sentAt(now()->subDay())
|
||||||
|
->status(InvoiceStatus::SENT)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$this->get(route('invoice.index'))
|
||||||
|
->assertInertiaPath('data.data.0.to_name', 'Familie Blabla')
|
||||||
|
->assertInertiaPath('data.data.0.sum_human', '33,00 €')
|
||||||
|
->assertInertiaPath('data.data.0.sent_at_human', now()->subDay()->format('d.m.Y'))
|
||||||
|
->assertInertiaPath('data.data.0.status', 'Rechnung gestellt')
|
||||||
|
->assertInertiaPath('data.meta.links.mass-store', route('invoice.mass-store'));
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ class MassStoreActionTest extends TestCase
|
||||||
{
|
{
|
||||||
Member::factory()->defaults()->emailBillKind()->create(['subscription_id' => null]);
|
Member::factory()->defaults()->emailBillKind()->create(['subscription_id' => null]);
|
||||||
|
|
||||||
$this->postJson(route('allpayment.store'), [
|
$this->postJson(route('invoice.mass-store'), [
|
||||||
'year' => now()->addYear()->year,
|
'year' => now()->addYear()->year,
|
||||||
])->assertOk();
|
])->assertOk();
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class MassStoreActionTest extends TestCase
|
||||||
{
|
{
|
||||||
Member::factory()->defaults()->create();
|
Member::factory()->defaults()->create();
|
||||||
|
|
||||||
$this->postJson(route('allpayment.store'), [
|
$this->postJson(route('invoice.mass-store'), [
|
||||||
'year' => now()->addYear()->year,
|
'year' => now()->addYear()->year,
|
||||||
])->assertOk();
|
])->assertOk();
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ class MassStoreActionTest extends TestCase
|
||||||
new Child('beitrag2 für {name} für {year}', 2290),
|
new Child('beitrag2 für {name} für {year}', 2290),
|
||||||
]))->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster', 'address' => 'Maxstr 4', 'zip' => '33445', 'location' => 'Solingen']);
|
]))->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster', 'address' => 'Maxstr 4', 'zip' => '33445', 'location' => 'Solingen']);
|
||||||
|
|
||||||
$this->postJson(route('allpayment.store'), [
|
$this->postJson(route('invoice.mass-store'), [
|
||||||
'year' => now()->addYear()->year,
|
'year' => now()->addYear()->year,
|
||||||
])->assertOk();
|
])->assertOk();
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ class MassStoreActionTest extends TestCase
|
||||||
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||||
Member::factory()->defaults()->for($subscription)->sameFamilyAs($member)->emailBillKind()->create(['firstname' => 'Jane']);
|
Member::factory()->defaults()->for($subscription)->sameFamilyAs($member)->emailBillKind()->create(['firstname' => 'Jane']);
|
||||||
|
|
||||||
$this->postJson(route('allpayment.store'), ['year' => now()->addYear()->year])->assertOk();
|
$this->postJson(route('invoice.mass-store'), ['year' => now()->addYear()->year])->assertOk();
|
||||||
|
|
||||||
$this->assertDatabaseCount('invoices', 1);
|
$this->assertDatabaseCount('invoices', 1);
|
||||||
$this->assertDatabaseCount('invoice_positions', 2);
|
$this->assertDatabaseCount('invoice_positions', 2);
|
||||||
|
@ -96,7 +96,7 @@ class MassStoreActionTest extends TestCase
|
||||||
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->create();
|
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->create();
|
||||||
Member::factory()->defaults()->for($subscription)->sameFamilyAs($member)->postBillKind()->create();
|
Member::factory()->defaults()->for($subscription)->sameFamilyAs($member)->postBillKind()->create();
|
||||||
|
|
||||||
$this->postJson(route('allpayment.store'), ['year' => now()->addYear()->year])->assertOk();
|
$this->postJson(route('invoice.mass-store'), ['year' => now()->addYear()->year])->assertOk();
|
||||||
|
|
||||||
$this->assertDatabaseCount('invoices', 2);
|
$this->assertDatabaseCount('invoices', 2);
|
||||||
$this->assertDatabaseCount('invoice_positions', 2);
|
$this->assertDatabaseCount('invoice_positions', 2);
|
||||||
|
|
|
@ -9,8 +9,10 @@ use App\User;
|
||||||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Testing\AssertableJsonString;
|
||||||
use Illuminate\Testing\TestResponse;
|
use Illuminate\Testing\TestResponse;
|
||||||
use Phake;
|
use Phake;
|
||||||
|
use PHPUnit\Framework\Assert;
|
||||||
use Symfony\Component\HttpFoundation\File\File;
|
use Symfony\Component\HttpFoundation\File\File;
|
||||||
use Tests\Lib\MakesHttpCalls;
|
use Tests\Lib\MakesHttpCalls;
|
||||||
use Tests\Lib\TestsInertia;
|
use Tests\Lib\TestsInertia;
|
||||||
|
@ -29,6 +31,7 @@ abstract class TestCase extends BaseTestCase
|
||||||
parent::setUp();
|
parent::setUp();
|
||||||
Auth::fake();
|
Auth::fake();
|
||||||
Member::disableGeolocation();
|
Member::disableGeolocation();
|
||||||
|
$this->initInertiaTestcase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loginNami(int $mglnr = 12345, string $password = 'password', int|Group $groupId = 55): self
|
public function loginNami(int $mglnr = 12345, string $password = 'password', int|Group $groupId = 55): self
|
||||||
|
@ -117,4 +120,17 @@ abstract class TestCase extends BaseTestCase
|
||||||
$this->assertCount(1, $output, 'Failed to parse output format of pdfinfo');
|
$this->assertCount(1, $output, 'Failed to parse output format of pdfinfo');
|
||||||
$this->assertEquals($pageCount, $output[0]);
|
$this->assertEquals($pageCount, $output[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initInertiaTestcase(): void
|
||||||
|
{
|
||||||
|
TestResponse::macro('assertInertiaPath', function ($path, $value) {
|
||||||
|
/** @var TestResponse */
|
||||||
|
$response = $this;
|
||||||
|
$props = data_get($response->viewData('page'), 'props');
|
||||||
|
Assert::assertNotNull($props);
|
||||||
|
$json = new AssertableJsonString($props);
|
||||||
|
$json->assertPath($path, $value);
|
||||||
|
return $this;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue