Add invoice full text search
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
c3a0381a0a
commit
784b49c3dd
|
@ -19,9 +19,9 @@ class InvoiceIndexAction
|
|||
/**
|
||||
* @return LengthAwarePaginator<Invoice>
|
||||
*/
|
||||
public function handle(InvoiceFilterScope $filter): LengthAwarePaginator
|
||||
public function handle(InvoiceFilterScope $filter)
|
||||
{
|
||||
return Invoice::withFilter($filter)->with('positions')->paginate(15);
|
||||
return $filter->getQuery()->query(fn ($q) => $q->with('positions'));
|
||||
}
|
||||
|
||||
public function asController(ActionRequest $request): Response
|
||||
|
@ -32,7 +32,7 @@ class InvoiceIndexAction
|
|||
$filter = InvoiceFilterScope::fromRequest($request->input('filter', ''));
|
||||
|
||||
return Inertia::render('invoice/Index', [
|
||||
'data' => InvoiceResource::collection($this->handle($filter)),
|
||||
'data' => InvoiceResource::collection($this->handle($filter)->paginate(15)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,14 @@ use Illuminate\Database\Eloquent\Collection;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Laravel\Scout\Searchable;
|
||||
use stdClass;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
/** @use HasFactory<InvoiceFactory> */
|
||||
use HasFactory;
|
||||
use Searchable;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
|
@ -154,4 +156,20 @@ class Invoice extends Model
|
|||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the indexable data array for the model.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toSearchableArray(): array
|
||||
{
|
||||
return [
|
||||
'to' => implode(', ', $this->to),
|
||||
'usage' => $this->usage,
|
||||
'greeting' => $this->greeting,
|
||||
'mail_email' => $this->mail_email,
|
||||
'status' => $this->status->value,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ namespace App\Invoice\Scopes;
|
|||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Lib\Filter;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use App\Lib\ScoutFilter;
|
||||
use Laravel\Scout\Builder;
|
||||
use Spatie\LaravelData\Attributes\MapInputName;
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||
|
@ -15,32 +16,32 @@ use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|||
*/
|
||||
#[MapInputName(SnakeCaseMapper::class)]
|
||||
#[MapOutputName(SnakeCaseMapper::class)]
|
||||
class InvoiceFilterScope extends Filter
|
||||
class InvoiceFilterScope extends ScoutFilter
|
||||
{
|
||||
/**
|
||||
* @param array<int, string> $statuses
|
||||
*/
|
||||
public function __construct(
|
||||
public ?array $statuses = null,
|
||||
public ?string $search = null
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function apply(Builder $query): Builder
|
||||
{
|
||||
$query = $query->whereIn('status', $this->statuses);
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function toDefault(): self
|
||||
{
|
||||
$this->statuses = $this->statuses === null ? InvoiceStatus::defaultVisibleValues()->toArray() : $this->statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getQuery(): Builder
|
||||
{
|
||||
$this->search = $this->search ?: '';
|
||||
|
||||
return Invoice::search($this->search, function ($engine, string $query, array $options) {
|
||||
if (empty($this->statuses)) {
|
||||
$filter = 'status = "asa6aeruuni4BahC7Wei6ahm1"';
|
||||
} else {
|
||||
$filter = collect($this->statuses)->map(fn (string $status) => "status = \"$status\"")->join(' OR ');
|
||||
}
|
||||
return $engine->search($query, [...$options, 'filter' => $filter]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Form\Models\Form;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Member\Member;
|
||||
|
||||
return [
|
||||
|
@ -153,7 +154,16 @@ return [
|
|||
'pagination' => [
|
||||
'maxTotalHits' => 1000000,
|
||||
]
|
||||
]
|
||||
],
|
||||
Invoice::class => [
|
||||
'filterableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'searchableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'sortableAttributes' => [],
|
||||
'displayedAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'pagination' => [
|
||||
'maxTotalHits' => 1000000,
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use App\Invoice\Models\Invoice;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Laravel\Scout\Console\SyncIndexSettingsCommand;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Artisan::call(SyncIndexSettingsCommand::class);
|
||||
foreach (Invoice::get() as $invoice) {
|
||||
$invoice->searchable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
|
@ -74,6 +74,7 @@
|
|||
</ui-popup>
|
||||
<page-filter breakpoint="xl" :filterable="false">
|
||||
<template #buttons>
|
||||
<f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text>
|
||||
<f-multipleselect
|
||||
id="statuses"
|
||||
:options="meta.statuses"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Invoice;
|
||||
namespace Tests\Feature\EndToEnd;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
|
@ -9,8 +9,11 @@ use App\Invoice\Models\InvoicePosition;
|
|||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\EndToEndTestCase;
|
||||
use Tests\Feature\Invoice\ReceiverRequestFactory;
|
||||
|
||||
uses(DatabaseTransactions::class);
|
||||
uses(EndToEndTestCase::class);
|
||||
|
||||
it('testItDisplaysInvoices', function () {
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
|
@ -25,6 +28,7 @@ it('testItDisplaysInvoices', function () {
|
|||
->status(InvoiceStatus::SENT)
|
||||
->create(['usage' => 'Usa', 'mail_email' => 'a@b.de']);
|
||||
|
||||
sleep(2);
|
||||
test()->get(route('invoice.index'))
|
||||
->assertInertiaPath('data.data.0.to.name', 'Familie Blabla')
|
||||
->assertInertiaPath('data.data.0.id', $invoice->id)
|
||||
|
@ -78,19 +82,35 @@ it('testValuesCanBeNull', function () {
|
|||
test()->login()->loginNami()->withoutExceptionHandling();
|
||||
Invoice::factory()->create();
|
||||
|
||||
sleep(2);
|
||||
test()->get(route('invoice.index'))
|
||||
->assertInertiaPath('data.data.0.sent_at_human', '');
|
||||
});
|
||||
|
||||
it('testItFiltersForInvoiceStatus', function () {
|
||||
it('filters for invoice status', function (array $filter, int $count) {
|
||||
test()->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();
|
||||
|
||||
test()->callFilter('invoice.index', [])->assertInertiaCount('data.data', 3);
|
||||
test()->callFilter('invoice.index', ['statuses' => []])->assertInertiaCount('data.data', 0);
|
||||
test()->callFilter('invoice.index', ['statuses' => ['Neu']])->assertInertiaCount('data.data', 1);
|
||||
test()->callFilter('invoice.index', ['statuses' => ['Neu', 'Rechnung beglichen']])->assertInertiaCount('data.data', 4);
|
||||
test()->callFilter('invoice.index', ['statuses' => ['Neu', 'Rechnung beglichen', 'Rechnung gestellt']])->assertInertiaCount('data.data', 6);
|
||||
});
|
||||
sleep(2);
|
||||
test()->callFilter('invoice.index', $filter)->assertInertiaCount('data.data', $count);
|
||||
})->with([
|
||||
[[], 3],
|
||||
[['statuses' => []], 0],
|
||||
[['statuses' => ['Neu']], 1],
|
||||
[['statuses' => ['Neu', 'Rechnung beglichen']], 4],
|
||||
[['statuses' => ['Neu', 'Rechnung beglichen', 'Rechnung gestellt']], 6],
|
||||
]);
|
||||
|
||||
it('searches invoice usage', function (array $filter, int $count) {
|
||||
test()->login()->loginNami()->withoutExceptionHandling();
|
||||
Invoice::factory()->status(InvoiceStatus::NEW)->create(['usage' => 'Kein Zweck']);
|
||||
Invoice::factory()->status(InvoiceStatus::NEW)->create(['usage' => 'Mitgliedsbeitrag']);
|
||||
|
||||
sleep(2);
|
||||
test()->callFilter('invoice.index', $filter)->assertInertiaCount('data.data', $count);
|
||||
})->with([
|
||||
[['search' => 'Mitgliedsbeitrag'], 1],
|
||||
[['search' => 'Kein'], 1],
|
||||
]);
|
|
@ -3,6 +3,7 @@
|
|||
namespace Tests;
|
||||
|
||||
use App\Form\Models\Form;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Member\Member;
|
||||
use Laravel\Scout\Console\FlushCommand;
|
||||
use Laravel\Scout\Console\SyncIndexSettingsCommand;
|
||||
|
@ -26,6 +27,7 @@ abstract class EndToEndTestCase extends TestCase
|
|||
config()->set('scout.driver', 'meilisearch');
|
||||
Artisan::call(FlushCommand::class, ['model' => Member::class]);
|
||||
Artisan::call(FlushCommand::class, ['model' => Form::class]);
|
||||
Artisan::call(FlushCommand::class, ['model' => Invoice::class]);
|
||||
Artisan::call(SyncIndexSettingsCommand::class);
|
||||
|
||||
return $this;
|
||||
|
|
Loading…
Reference in New Issue