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 = [
|
||||
'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' => [
|
||||
'index' => route('member.index'),
|
||||
'create' => route('member.create'),
|
||||
'allpayment' => route('allpayment.page'),
|
||||
'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->string('greeting');
|
||||
$table->string('status');
|
||||
$table->date('sent_at')->nullable();
|
||||
$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\NamiLoginCheckAction;
|
||||
use App\Initialize\Actions\NamiSearchAction;
|
||||
use App\Invoice\Actions\InvoiceIndexAction;
|
||||
use App\Invoice\Actions\MassStoreAction;
|
||||
use App\Maildispatcher\Actions\CreateAction;
|
||||
use App\Maildispatcher\Actions\DestroyAction;
|
||||
|
@ -46,8 +47,6 @@ use App\Membership\Actions\MembershipDestroyAction;
|
|||
use App\Membership\Actions\MembershipStoreAction;
|
||||
use App\Membership\Actions\MembershipUpdateAction;
|
||||
use App\Membership\Actions\StoreForGroupAction;
|
||||
use App\Payment\Actions\AllpaymentPageAction;
|
||||
use App\Payment\Actions\AllpaymentStoreAction;
|
||||
use App\Payment\Actions\DisplayPdfAction;
|
||||
use App\Payment\Actions\IndexAction as PaymentIndexAction;
|
||||
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');
|
||||
|
||||
// -------------------------------- allpayment ---------------------------------
|
||||
Route::get('allpayment', AllpaymentPageAction::class)->name('allpayment.page');
|
||||
Route::post('allpayment', MassStoreAction::class)->name('allpayment.store');
|
||||
Route::post('/invoice/mass-store', MassStoreAction::class)->name('invoice.mass-store');
|
||||
|
||||
// ---------------------------------- invoice ----------------------------------
|
||||
Route::get('/invoice', InvoiceIndexAction::class)->name('invoice.index');
|
||||
Route::post('/invoice', InvoiceStoreAction::class)->name('invoice.store');
|
||||
|
||||
// --------------------------------- 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]);
|
||||
|
||||
$this->postJson(route('allpayment.store'), [
|
||||
$this->postJson(route('invoice.mass-store'), [
|
||||
'year' => now()->addYear()->year,
|
||||
])->assertOk();
|
||||
|
||||
|
@ -35,7 +35,7 @@ class MassStoreActionTest extends TestCase
|
|||
{
|
||||
Member::factory()->defaults()->create();
|
||||
|
||||
$this->postJson(route('allpayment.store'), [
|
||||
$this->postJson(route('invoice.mass-store'), [
|
||||
'year' => now()->addYear()->year,
|
||||
])->assertOk();
|
||||
|
||||
|
@ -50,7 +50,7 @@ class MassStoreActionTest extends TestCase
|
|||
new Child('beitrag2 für {name} für {year}', 2290),
|
||||
]))->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,
|
||||
])->assertOk();
|
||||
|
||||
|
@ -82,7 +82,7 @@ class MassStoreActionTest extends TestCase
|
|||
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
|
||||
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('invoice_positions', 2);
|
||||
|
@ -96,7 +96,7 @@ class MassStoreActionTest extends TestCase
|
|||
$member = Member::factory()->defaults()->for($subscription)->emailBillKind()->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('invoice_positions', 2);
|
||||
|
|
|
@ -9,8 +9,10 @@ use App\User;
|
|||
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Testing\AssertableJsonString;
|
||||
use Illuminate\Testing\TestResponse;
|
||||
use Phake;
|
||||
use PHPUnit\Framework\Assert;
|
||||
use Symfony\Component\HttpFoundation\File\File;
|
||||
use Tests\Lib\MakesHttpCalls;
|
||||
use Tests\Lib\TestsInertia;
|
||||
|
@ -29,6 +31,7 @@ abstract class TestCase extends BaseTestCase
|
|||
parent::setUp();
|
||||
Auth::fake();
|
||||
Member::disableGeolocation();
|
||||
$this->initInertiaTestcase();
|
||||
}
|
||||
|
||||
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->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