Add filter for invoices
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
30d670a575
commit
c0daff972b
|
@ -5,9 +5,11 @@ namespace App\Invoice\Actions;
|
|||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Resources\InvoiceResource;
|
||||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
|
||||
class InvoiceIndexAction
|
||||
{
|
||||
|
@ -17,18 +19,20 @@ class InvoiceIndexAction
|
|||
/**
|
||||
* @return LengthAwarePaginator<Invoice>
|
||||
*/
|
||||
public function handle(): LengthAwarePaginator
|
||||
public function handle(InvoiceFilterScope $filter): LengthAwarePaginator
|
||||
{
|
||||
return Invoice::select('*')->with('positions')->paginate(15);
|
||||
return Invoice::withFilter($filter)->with('positions')->paginate(15);
|
||||
}
|
||||
|
||||
public function asController(): Response
|
||||
public function asController(ActionRequest $request): Response
|
||||
{
|
||||
session()->put('menu', 'invoice');
|
||||
session()->put('title', 'Rechnungen');
|
||||
|
||||
$filter = InvoiceFilterScope::fromRequest($request->input('filter', ''));
|
||||
|
||||
return Inertia::render('invoice/Index', [
|
||||
'data' => InvoiceResource::collection($this->handle()),
|
||||
'data' => InvoiceResource::collection($this->handle($filter)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,23 @@ enum InvoiceStatus: string
|
|||
return collect(static::cases())->map(fn ($case) => $case->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection<int, string>
|
||||
*/
|
||||
public static function defaultVisibleValues(): Collection
|
||||
{
|
||||
return collect(static::cases())->filter(fn ($value) => $value->defaultVisible())->map(fn ($case) => $case->value);
|
||||
}
|
||||
|
||||
public function defaultVisible(): bool
|
||||
{
|
||||
return match ($this) {
|
||||
static::NEW => true,
|
||||
static::SENT => true,
|
||||
static::PAID => false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int, array{id: string, name: string}>
|
||||
*/
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Invoice\BillKind;
|
|||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\InvoiceDocument;
|
||||
use App\Invoice\RememberDocument;
|
||||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
|
@ -119,6 +120,15 @@ class Invoice extends Model
|
|||
->where('last_remembered_at', '<=', now()->subMonths(3));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<self> $query
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeWithFilter(Builder $query, InvoiceFilterScope $filter): Builder
|
||||
{
|
||||
return $filter->apply($query);
|
||||
}
|
||||
|
||||
public function getMailRecipient(): stdClass
|
||||
{
|
||||
return (object) [
|
||||
|
|
|
@ -5,6 +5,7 @@ namespace App\Invoice\Resources;
|
|||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
use App\Lib\HasMeta;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
|
@ -62,6 +63,7 @@ class InvoiceResource extends JsonResource
|
|||
'statuses' => InvoiceStatus::forSelect(),
|
||||
'members' => Member::forSelect(),
|
||||
'subscriptions' => Subscription::forSelect(),
|
||||
'filter' => InvoiceFilterScope::fromRequest(request()->input('filter', '')),
|
||||
'default' => [
|
||||
'to' => [
|
||||
'name' => '',
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Scopes;
|
||||
|
||||
use App\Form\Models\Form;
|
||||
use App\Form\Models\Participant;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Lib\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
|
||||
/**
|
||||
* @extends Filter<Participant>
|
||||
*/
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
class InvoiceFilterScope extends Filter
|
||||
{
|
||||
/**
|
||||
* @param array<int, string> $statuses
|
||||
*/
|
||||
public function __construct(
|
||||
public ?array $statuses = null,
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function apply(Builder $query): Builder
|
||||
{
|
||||
$query = $query->whereIn('status', $this->statuses);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function toDefault(): self
|
||||
{
|
||||
$this->statuses = $this->statuses === null ? InvoiceStatus::defaultVisibleValues()->toArray() : $this->statuses;
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -36,6 +36,11 @@ abstract class Filter extends Data
|
|||
*/
|
||||
public static function fromPost(?array $post = null): static
|
||||
{
|
||||
return static::withoutMagicalCreationFrom($post ?: []);
|
||||
return static::withoutMagicalCreationFrom($post ?: [])->toDefault();
|
||||
}
|
||||
|
||||
public function toDefault(): self
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,17 @@
|
|||
</section>
|
||||
</form>
|
||||
</ui-popup>
|
||||
<page-filter breakpoint="xl">
|
||||
<f-multipleselect
|
||||
id="statuses"
|
||||
:options="meta.statuses"
|
||||
:model-value="getFilter('statuses')"
|
||||
label="Status"
|
||||
size="sm"
|
||||
name="group_ids"
|
||||
@update:model-value="setFilter('statuses', $event)"
|
||||
></f-multipleselect>
|
||||
</page-filter>
|
||||
<table cellspacing="0" cellpadding="0" border="0" class="custom-table custom-table-sm">
|
||||
<thead>
|
||||
<th>Empfänger</th>
|
||||
|
@ -118,7 +129,7 @@
|
|||
import {ref} from 'vue';
|
||||
import {indexProps, useIndex} from '../../composables/useInertiaApiIndex.js';
|
||||
const props = defineProps(indexProps);
|
||||
var {axios, meta, data, reloadPage, create, single, edit, cancel, submit, remove} = useIndex(props.data, 'invoice');
|
||||
var {axios, meta, data, reloadPage, create, single, edit, cancel, submit, remove, getFilter, setFilter} = useIndex(props.data, 'invoice');
|
||||
const massstore = ref(null);
|
||||
const deleting = ref(null);
|
||||
const forMember = ref({member_id: null, subscription_id: null, year: null});
|
||||
|
|
|
@ -56,6 +56,7 @@ class InvoiceIndexActionTest extends TestCase
|
|||
->assertInertiaPath('data.meta.statuses.0', ['id' => 'Neu', 'name' => 'Neu'])
|
||||
->assertInertiaPath('data.meta.members.0', ['id' => $member->id, 'name' => 'Aaaa Aaab'])
|
||||
->assertInertiaPath('data.meta.subscriptions.0', ['name' => 'Beitrag', 'id' => $subscription->id])
|
||||
->assertInertiaPath('data.meta.filter.statuses', ['Neu', 'Rechnung gestellt'])
|
||||
->assertInertiaPath('data.meta.default', [
|
||||
'to' => [
|
||||
'name' => '',
|
||||
|
@ -86,4 +87,18 @@ class InvoiceIndexActionTest extends TestCase
|
|||
$this->get(route('invoice.index'))
|
||||
->assertInertiaPath('data.data.0.sent_at_human', '');
|
||||
}
|
||||
|
||||
public function testItFiltersForInvoiceStatus(): void
|
||||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
Invoice::factory()->status(InvoiceStatus::NEW)->create();
|
||||
Invoice::factory()->status(InvoiceStatus::SENT)->count(2)->create();
|
||||
Invoice::factory()->status(InvoiceStatus::PAID)->count(3)->create();
|
||||
|
||||
$this->callFilter('invoice.index', [])->assertInertiaCount('data.data', 3);
|
||||
$this->callFilter('invoice.index', ['statuses' => []])->assertInertiaCount('data.data', 0);
|
||||
$this->callFilter('invoice.index', ['statuses' => ['Neu']])->assertInertiaCount('data.data', 1);
|
||||
$this->callFilter('invoice.index', ['statuses' => ['Neu', 'Rechnung beglichen']])->assertInertiaCount('data.data', 4);
|
||||
$this->callFilter('invoice.index', ['statuses' => ['Neu', 'Rechnung beglichen', 'Rechnung gestellt']])->assertInertiaCount('data.data', 6);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue