Add update for invoices
This commit is contained in:
parent
0b9eb77e77
commit
c598508ceb
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Actions;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
trait HasValidation
|
||||
{
|
||||
/**
|
||||
* @return array<string, string|array<int, string|Rule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'status' => ['required', 'string', 'max:255', Rule::in(InvoiceStatus::values())],
|
||||
'via' => ['required', 'string', 'max:255', Rule::in(BillKind::values())],
|
||||
'to' => 'array',
|
||||
'to.address' => 'required|string|max:255',
|
||||
'to.location' => 'required|string|max:255',
|
||||
'to.zip' => 'required|string|max:255',
|
||||
'to.name' => 'required|string|max:255',
|
||||
'greeting' => 'required|string|max:255',
|
||||
'positions' => 'array',
|
||||
'positions.*.description' => 'required|string|max:300',
|
||||
'positions.*.price' => 'required|integer|min:0',
|
||||
'positions.*.member_id' => 'required|exists:members,id',
|
||||
'positions.*.id' => 'present|nullable|exists:invoice_positions,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getValidationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'to.address' => 'Adresse',
|
||||
'to.name' => 'Name',
|
||||
'to.zip' => 'PLZ',
|
||||
'to.location' => 'Ort',
|
||||
'status' => 'Status',
|
||||
'via' => 'Rechnungsweg',
|
||||
];
|
||||
}
|
||||
}
|
|
@ -2,53 +2,15 @@
|
|||
|
||||
namespace App\Invoice\Actions;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Lib\Events\Succeeded;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class InvoiceStoreAction
|
||||
{
|
||||
use AsAction;
|
||||
|
||||
/**
|
||||
* @return array<string, string|array<int, string|Rule>>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'status' => ['required', 'string', 'max:255', Rule::in(InvoiceStatus::values())],
|
||||
'via' => ['required', 'string', 'max:255', Rule::in(BillKind::values())],
|
||||
'to' => 'array',
|
||||
'to.address' => 'required|string|max:255',
|
||||
'to.location' => 'required|string|max:255',
|
||||
'to.zip' => 'required|string|max:255',
|
||||
'to.name' => 'required|string|max:255',
|
||||
'greeting' => 'required|string|max:255',
|
||||
'positions' => 'array',
|
||||
'positions.*.description' => 'required|string|max:300',
|
||||
'positions.*.price' => 'required|integer|min:0',
|
||||
'positions.*.member_id' => 'required|exists:members,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getValidationAttributes(): array
|
||||
{
|
||||
return [
|
||||
'to.address' => 'Adresse',
|
||||
'to.name' => 'Name',
|
||||
'to.zip' => 'PLZ',
|
||||
'to.location' => 'Ort',
|
||||
'status' => 'Status',
|
||||
'via' => 'Rechnungsweg',
|
||||
];
|
||||
}
|
||||
use HasValidation;
|
||||
|
||||
public function handle(ActionRequest $request): void
|
||||
{
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Actions;
|
||||
|
||||
use Lorisleiva\Actions\ActionRequest;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Lib\Events\Succeeded;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class InvoiceUpdateAction
|
||||
{
|
||||
use AsAction;
|
||||
use HasValidation;
|
||||
|
||||
public function handle(Invoice $invoice, ActionRequest $request): void
|
||||
{
|
||||
$invoice->update($request->safe()->except('positions'));
|
||||
|
||||
foreach ($request->validated('positions') as $position) {
|
||||
if ($position['id']) {
|
||||
$invoice->positions()->firstWhere('id', $position['id'])->update(Arr::except($position, 'id'));
|
||||
continue;
|
||||
}
|
||||
|
||||
$invoice->positions()->create($position);
|
||||
}
|
||||
|
||||
$invoice->positions()->whereNotIn('id', array_column($request->validated('positions'), 'id'))->delete();
|
||||
|
||||
Succeeded::message('Rechnung bearbeitet.')->dispatch();
|
||||
}
|
||||
}
|
|
@ -2,12 +2,22 @@
|
|||
|
||||
namespace App\Invoice\Models;
|
||||
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class InvoicePosition extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $guarded = [];
|
||||
|
||||
/**
|
||||
* @return BelongsTo<Member>
|
||||
*/
|
||||
public function member(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Member::class);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Resources;
|
||||
|
||||
use App\Invoice\Models\InvoicePosition;
|
||||
use Illuminate\Http\Resources\Json\JsonResource;
|
||||
|
||||
/**
|
||||
* @mixin InvoicePosition
|
||||
*/
|
||||
class InvoicePositionResource extends JsonResource
|
||||
{
|
||||
|
||||
/**
|
||||
* Transform the resource into an array.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'id' => $this->id,
|
||||
'price' => $this->price,
|
||||
'member_id' => $this->member_id,
|
||||
'description' => $this->description,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -26,11 +26,17 @@ class InvoiceResource extends JsonResource
|
|||
public function toArray($request)
|
||||
{
|
||||
return [
|
||||
'to_name' => $this->to['name'],
|
||||
'id' => $this->id,
|
||||
'to' => $this->to,
|
||||
'sum_human' => number_format($this->positions->sum('price') / 100, 2, ',', '') . ' €',
|
||||
'sent_at_human' => $this->sent_at?->format('d.m.Y') ?: '',
|
||||
'status' => $this->status->value,
|
||||
'via' => $this->via->value,
|
||||
'positions' => InvoicePositionResource::collection($this->whenLoaded('positions')),
|
||||
'greeting' => $this->greeting,
|
||||
'links' => [
|
||||
'update' => route('invoice.update', ['invoice' => $this->getModel()]),
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -60,6 +66,7 @@ class InvoiceResource extends JsonResource
|
|||
'via' => null,
|
||||
],
|
||||
'default_position' => [
|
||||
'id' => null,
|
||||
'price' => 0,
|
||||
'description' => '',
|
||||
'member_id' => null,
|
||||
|
|
|
@ -23,6 +23,7 @@ class InvoicePositionFactory extends Factory
|
|||
return [
|
||||
'description' => $this->faker->words(4, true),
|
||||
'member_id' => Member::factory()->defaults()->create()->id,
|
||||
'price' => $this->faker->numberBetween(1000, 2000),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,8 @@
|
|||
</section>
|
||||
</form>
|
||||
</ui-popup>
|
||||
<ui-popup v-if="single !== null" heading="Rechnung erstellen" inner-width="max-w-4xl" @close="cancel">
|
||||
<ui-popup v-if="single !== null" :heading="`Rechnung ${single.id ? 'bearbeiten' : 'erstellen'}`"
|
||||
inner-width="max-w-4xl" @close="cancel">
|
||||
<form class="grid grid-cols-2 gap-3 mt-4" @submit.prevent="submit">
|
||||
<ui-box heading="Empfänger" container-class="grid grid-cols-2 gap-3">
|
||||
<f-text id="to_name" v-model="single.to.name" name="to_name" label="Name" class="col-span-full"
|
||||
|
@ -67,7 +68,7 @@
|
|||
|
||||
<tr v-for="(invoice, index) in data" :key="index">
|
||||
<td>
|
||||
<div v-text="invoice.to_name"></div>
|
||||
<div v-text="invoice.to.name"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div v-text="invoice.sum_human"></div>
|
||||
|
@ -81,7 +82,10 @@
|
|||
<td>
|
||||
<div v-text="invoice.via"></div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td>
|
||||
<a v-tooltip="`Bearbeiten`" href="#" class="inline-flex btn btn-warning btn-sm"
|
||||
@click.prevent="edit(invoice)"><ui-sprite src="pencil"></ui-sprite></a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="px-6">
|
||||
|
@ -94,7 +98,7 @@
|
|||
import { ref } from 'vue';
|
||||
import { indexProps, useIndex } from '../../composables/useInertiaApiIndex.js';
|
||||
const props = defineProps(indexProps);
|
||||
var { axios, meta, data, reloadPage, create, single, cancel, submit } = useIndex(props.data, 'invoice');
|
||||
var { axios, meta, data, reloadPage, create, single, edit, cancel, submit } = useIndex(props.data, 'invoice');
|
||||
const massstore = ref(null);
|
||||
|
||||
async function sendMassstore() {
|
||||
|
|
|
@ -26,6 +26,7 @@ use App\Initialize\Actions\NamiGetSearchLayerAction;
|
|||
use App\Initialize\Actions\NamiLoginCheckAction;
|
||||
use App\Initialize\Actions\NamiSearchAction;
|
||||
use App\Invoice\Actions\InvoiceIndexAction;
|
||||
use App\Invoice\Actions\InvoiceUpdateAction;
|
||||
use App\Invoice\Actions\MassStoreAction;
|
||||
use App\Maildispatcher\Actions\CreateAction;
|
||||
use App\Maildispatcher\Actions\DestroyAction;
|
||||
|
@ -117,6 +118,7 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
|||
// ---------------------------------- invoice ----------------------------------
|
||||
Route::get('/invoice', InvoiceIndexAction::class)->name('invoice.index');
|
||||
Route::post('/invoice', InvoiceStoreAction::class)->name('invoice.store');
|
||||
Route::patch('/invoice/{invoice}', InvoiceUpdateAction::class)->name('invoice.update');
|
||||
|
||||
// --------------------------------- membership --------------------------------
|
||||
Route::get('/member/{member}/membership', MembershipIndexAction::class)->name('member.membership.index');
|
||||
|
|
|
@ -6,6 +6,7 @@ use App\Invoice\BillKind;
|
|||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Models\InvoicePosition;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
@ -18,8 +19,8 @@ class InvoiceIndexActionTest extends TestCase
|
|||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
$member = Member::factory()->defaults()->create(['firstname' => 'Aaaa', 'lastname' => 'Aaab']);
|
||||
Invoice::factory()
|
||||
->has(InvoicePosition::factory()->price(1100), 'positions')
|
||||
$invoice = Invoice::factory()
|
||||
->has(InvoicePosition::factory()->price(1100)->for($member)->state(['description' => 'lala']), 'positions')
|
||||
->has(InvoicePosition::factory()->price(2200), 'positions')
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla'))
|
||||
->sentAt(now()->subDay())
|
||||
|
@ -28,11 +29,18 @@ class InvoiceIndexActionTest extends TestCase
|
|||
->create();
|
||||
|
||||
$this->get(route('invoice.index'))
|
||||
->assertInertiaPath('data.data.0.to_name', 'Familie Blabla')
|
||||
->assertInertiaPath('data.data.0.to.name', 'Familie Blabla')
|
||||
->assertInertiaPath('data.data.0.id', $invoice->id)
|
||||
->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.data.0.via', 'Post')
|
||||
->assertInertiaPath('data.data.0.greeting', $invoice->greeting)
|
||||
->assertInertiaPath('data.data.0.positions.0.price', 1100)
|
||||
->assertInertiaPath('data.data.0.positions.0.member_id', $member->id)
|
||||
->assertInertiaPath('data.data.0.positions.0.description', 'lala')
|
||||
->assertInertiaPath('data.data.0.positions.0.id', $invoice->positions->first()->id)
|
||||
->assertInertiaPath('data.data.0.links.update', route('invoice.update', ['invoice' => $invoice]))
|
||||
->assertInertiaPath('data.meta.links.mass-store', route('invoice.mass-store'))
|
||||
->assertInertiaPath('data.meta.links.store', route('invoice.store'))
|
||||
->assertInertiaPath('data.meta.vias.0', ['id' => 'E-Mail', 'name' => 'E-Mail'])
|
||||
|
@ -51,8 +59,10 @@ class InvoiceIndexActionTest extends TestCase
|
|||
'via' => null,
|
||||
])
|
||||
->assertInertiaPath('data.meta.default_position', [
|
||||
'id' => null,
|
||||
'price' => 0,
|
||||
'description' => '',
|
||||
'member_id' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ class InvoicePositionRequestFactory extends RequestFactory
|
|||
return [
|
||||
'description' => 'Beitrag Abc',
|
||||
'price' => 3250,
|
||||
'member_id' => Member::factory()->defaults()->create()->id,
|
||||
'id' => null,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -29,4 +31,9 @@ class InvoicePositionRequestFactory extends RequestFactory
|
|||
{
|
||||
return $this->state(['member_id' => $member->id]);
|
||||
}
|
||||
|
||||
public function id(int $id): self
|
||||
{
|
||||
return $this->state(['id' => $id]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,16 @@ use Worksome\RequestFactories\RequestFactory;
|
|||
|
||||
class InvoiceRequestFactory extends RequestFactory
|
||||
{
|
||||
/** @var array<int, InvoicePositionRequestFactory> */
|
||||
public $positions = [];
|
||||
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'to' => ReceiverRequestFactory::new(),
|
||||
'greeting' => 'Hallo Familie',
|
||||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => BillKind::EMAIL->value,
|
||||
'positions' => []
|
||||
];
|
||||
}
|
||||
|
@ -29,9 +34,17 @@ class InvoiceRequestFactory extends RequestFactory
|
|||
|
||||
public function position(InvoicePositionRequestFactory $factory): self
|
||||
{
|
||||
return $this->state(['positions' => [
|
||||
$factory->create(),
|
||||
]]);
|
||||
$this->positions[] = $factory;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function create(array $attributes = []): array
|
||||
{
|
||||
return parent::create([
|
||||
'positions' => array_map(fn ($position) => $position->create(), $this->positions),
|
||||
...$attributes,
|
||||
]);
|
||||
}
|
||||
|
||||
public function via(BillKind $via): self
|
||||
|
|
|
@ -24,12 +24,12 @@ class InvoiceStoreActionTest extends TestCase
|
|||
route('invoice.store'),
|
||||
InvoiceRequestFactory::new()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen'))
|
||||
->position(InvoicePositionRequestFactory::new()->description('Beitrag Abc')->price(3250)->member($member))
|
||||
->status(InvoiceStatus::PAID)
|
||||
->via(BillKind::POST)
|
||||
->state([
|
||||
'greeting' => 'Hallo Familie',
|
||||
])
|
||||
->position(InvoicePositionRequestFactory::new()->description('Beitrag Abc')->price(3250)->member($member))
|
||||
->create()
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature\Invoice;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Models\InvoicePosition;
|
||||
use App\Member\Member;
|
||||
use Generator;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Tests\TestCase;
|
||||
|
||||
class InvoiceUpdateActionTest extends TestCase
|
||||
{
|
||||
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function testItCanUpdateAnInvoice(): void
|
||||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
$invoice = Invoice::factory()->create();
|
||||
|
||||
$this->patchJson(
|
||||
route('invoice.update', ['invoice' => $invoice]),
|
||||
InvoiceRequestFactory::new()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen'))
|
||||
->status(InvoiceStatus::PAID)
|
||||
->via(BillKind::POST)
|
||||
->state([
|
||||
'greeting' => 'Hallo Familie',
|
||||
])
|
||||
->create()
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseCount('invoices', 1);
|
||||
$this->assertDatabaseHas('invoices', [
|
||||
'greeting' => 'Hallo Familie',
|
||||
'via' => BillKind::POST->value,
|
||||
'status' => InvoiceStatus::PAID->value,
|
||||
'id' => $invoice->id,
|
||||
]);
|
||||
$invoice = Invoice::firstWhere('greeting', 'Hallo Familie');
|
||||
$this->assertEquals([
|
||||
'name' => 'Familie Blabla',
|
||||
'address' => 'Musterstr 44',
|
||||
'zip' => '22222',
|
||||
'location' => 'Solingen',
|
||||
], $invoice->to);
|
||||
}
|
||||
|
||||
public function testItAddsAPosition(): void
|
||||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
$invoice = Invoice::factory()->create();
|
||||
|
||||
$this->patchJson(
|
||||
route('invoice.update', ['invoice' => $invoice]),
|
||||
InvoiceRequestFactory::new()
|
||||
->position(InvoicePositionRequestFactory::new())
|
||||
->position(InvoicePositionRequestFactory::new())
|
||||
->create()
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseCount('invoice_positions', 2);
|
||||
}
|
||||
|
||||
public function testItUpdatesAPosition(): void
|
||||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
$invoice = Invoice::factory()->has(InvoicePosition::factory(), 'positions')->create();
|
||||
|
||||
$this->patchJson(
|
||||
route('invoice.update', ['invoice' => $invoice]),
|
||||
InvoiceRequestFactory::new()
|
||||
->position(InvoicePositionRequestFactory::new()->description('la')->id($invoice->positions->first()->id))
|
||||
->create()
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseCount('invoice_positions', 1);
|
||||
$this->assertDatabaseHas('invoice_positions', [
|
||||
'description' => 'la',
|
||||
'id' => $invoice->positions->first()->id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function testItDeletesAPosition(): void
|
||||
{
|
||||
$this->login()->loginNami()->withoutExceptionHandling();
|
||||
$invoice = Invoice::factory()->has(InvoicePosition::factory(), 'positions')->create();
|
||||
|
||||
$this->patchJson(
|
||||
route('invoice.update', ['invoice' => $invoice]),
|
||||
InvoiceRequestFactory::new()
|
||||
->create()
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseCount('invoice_positions', 0);
|
||||
}
|
||||
|
||||
public function validationDataProvider(): Generator
|
||||
{
|
||||
yield [
|
||||
['to.address' => ''],
|
||||
['to.address' => 'Adresse ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['to.name' => ''],
|
||||
['to.name' => 'Name ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['to.location' => ''],
|
||||
['to.location' => 'Ort ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['status' => ''],
|
||||
['status' => 'Status ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['status' => 'lala'],
|
||||
['status' => 'Der gewählte Wert für Status ist ungültig.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['to.zip' => ''],
|
||||
['to.zip' => 'PLZ ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['via' => ''],
|
||||
['via' => 'Rechnungsweg ist erforderlich.']
|
||||
];
|
||||
|
||||
yield [
|
||||
['via' => 'lala'],
|
||||
['via' => 'Der gewählte Wert für Rechnungsweg ist ungültig.']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $input
|
||||
* @param array<string, string> $errors
|
||||
* @dataProvider validationDataProvider
|
||||
*/
|
||||
public function testItValidatesInput(array $input, array $errors): void
|
||||
{
|
||||
$this->login()->loginNami();
|
||||
|
||||
$response = $this->postJson(
|
||||
route('invoice.store'),
|
||||
InvoiceRequestFactory::new()
|
||||
->to(ReceiverRequestFactory::new())
|
||||
->position(InvoicePositionRequestFactory::new()->member(Member::factory()->defaults()->create()))
|
||||
->via(BillKind::POST)
|
||||
->state($input)
|
||||
->create()
|
||||
);
|
||||
|
||||
$response->assertJsonValidationErrors($errors);
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ namespace Illuminate\Testing;
|
|||
use Symfony\Component\HttpFoundation\File\File;
|
||||
|
||||
/**
|
||||
* @method self assertInertiaPath(string $path, string $value)
|
||||
* @method self assertInertiaPath(string $path, string|array<string, mixed> $value)
|
||||
* @method File getFile()
|
||||
*/
|
||||
class TestResponse
|
||||
|
|
Loading…
Reference in New Issue