Add frontend for subscription children
continuous-integration/drone/push Build is passing Details

This commit is contained in:
philipp lang 2022-12-13 23:11:32 +01:00
parent 20b1309ccc
commit 49417981f1
28 changed files with 407 additions and 105 deletions

View File

@ -22,7 +22,6 @@ class InitializeFees
Fee::create(['nami_id' => $fee->id, 'name' => $fee->name]) Fee::create(['nami_id' => $fee->id, 'name' => $fee->name])
->subscriptions()->create([ ->subscriptions()->create([
'name' => $fee->name, 'name' => $fee->name,
'amount' => 1000,
]); ]);
}); });
} }

View File

@ -49,7 +49,7 @@ class Page
return $this->getPayments()->mapWithKeys(function (Payment $payment) { return $this->getPayments()->mapWithKeys(function (Payment $payment) {
$key = "Beitrag {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname} ({$payment->subscription->name})"; $key = "Beitrag {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname} ({$payment->subscription->name})";
return [$key => $this->number($payment->subscription->amount)]; return [$key => $this->number($payment->subscription->getAmount())];
})->toArray(); })->toArray();
} }

View File

@ -4,7 +4,6 @@ namespace App\Letter;
use App\Payment\Payment; use App\Payment\Payment;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
class RememberDocument extends Letter class RememberDocument extends Letter
{ {
@ -18,57 +17,14 @@ class RememberDocument extends Letter
return 'Zahlungserinnerung'; return 'Zahlungserinnerung';
} }
public function setFilename(string $filename): static
{
$this->filename = $filename;
return $this;
}
public function getFilename(): string
{
return $this->filename;
}
public function view(): string public function view(): string
{ {
return 'tex.remember'; return 'tex.remember';
} }
/** public static function sendAllLabel(): string
* @return array<string, string>
*/
public function getPositions(Collection $page): array
{ {
$memberIds = $page->pluck('id')->toArray(); return 'Erinnerungen versenden';
$payments = Payment::whereIn('member_id', $memberIds)
->orderByRaw('nr, member_id')->whereNeedsRemember()->get();
return $payments->mapWithKeys(function (Payment $payment) {
$key = "Beitrag {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname} ({$payment->subscription->name})";
return [$key => $this->number($payment->subscription->amount)];
})->toArray();
}
public function getAddress(Collection $page): string
{
return $page->first()->address;
}
public function getZip(Collection $page): string
{
return $page->first()->zip;
}
public function getEmail(Collection $page): string
{
return $page->first()->email_parents ?: $page->first()->email;
}
public function getLocation(Collection $page): string
{
return $page->first()->location;
} }
public function afterSingle(Payment $payment): void public function afterSingle(Payment $payment): void
@ -103,9 +59,4 @@ class RememberDocument extends Letter
'Das zuletzt erinnerte Datum wird auf heute gesetzt.', 'Das zuletzt erinnerte Datum wird auf heute gesetzt.',
]; ];
} }
public static function sendAllLabel(): string
{
return 'Erinnerungen versenden';
}
} }

View File

@ -20,7 +20,7 @@ class MemberShowAction
return [ return [
'data' => new MemberResource($member 'data' => new MemberResource($member
->load('memberships') ->load('memberships')
->load('payments.subscription') ->load('payments.subscription.children')
->load('nationality') ->load('nationality')
->load('region') ->load('region')
->load('subscription') ->load('subscription')

View File

@ -280,10 +280,11 @@ class Member extends Model
public function scopeWithPendingPayment(Builder $q): Builder public function scopeWithPendingPayment(Builder $q): Builder
{ {
return $q->addSelect([ return $q->addSelect([
'pending_payment' => Payment::selectRaw('SUM(subscriptions.amount)') 'pending_payment' => Payment::selectRaw('SUM(subscription_children.amount)')
->whereColumn('payments.member_id', 'members.id') ->whereColumn('payments.member_id', 'members.id')
->whereNeedsPayment() ->whereNeedsPayment()
->join('subscriptions', 'subscriptions.id', 'payments.subscription_id'), ->join('subscriptions', 'subscriptions.id', 'payments.subscription_id')
->join('subscription_children', 'subscriptions.id', 'subscription_children.parent_id'),
]); ]);
} }

View File

@ -12,7 +12,11 @@ class MemberPaymentBlock extends Block
*/ */
public function data(): array public function data(): array
{ {
$amount = Payment::whereNeedsPayment()->selectRaw('sum(subscriptions.amount) AS nr')->join('subscriptions', 'subscriptions.id', 'payments.subscription_id')->first(); $amount = Payment::whereNeedsPayment()
->selectRaw('sum(subscription_children.amount) AS nr')
->join('subscriptions', 'subscriptions.id', 'payments.subscription_id')
->join('subscription_children', 'subscriptions.id', 'subscription_children.parent_id')
->first();
$members = Member::whereHasPendingPayment()->count(); $members = Member::whereHasPendingPayment()->count();
return [ return [

View File

@ -6,6 +6,7 @@ use App\Fee;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Subscription extends Model class Subscription extends Model
{ {
@ -13,8 +14,23 @@ class Subscription extends Model
public $fillable = ['name', 'amount', 'fee_id']; public $fillable = ['name', 'amount', 'fee_id'];
public function getAmount(): int
{
return $this->children->sum('amount');
}
public function fee(): BelongsTo public function fee(): BelongsTo
{ {
return $this->belongsTo(Fee::class); return $this->belongsTo(Fee::class);
} }
public function children(): HasMany
{
return $this->hasMany(SubscriptionChild::class, 'parent_id');
}
public static function booted(): void
{
static::deleting(fn ($model) => $model->children()->delete());
}
} }

View File

@ -0,0 +1,17 @@
<?php
namespace App\Payment;
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class SubscriptionChild extends Model
{
use HasFactory;
use HasUuids;
public $timestamps = false;
public $fillable = ['name', 'amount', 'uuid', 'parent_id'];
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Payment;
use Illuminate\Http\Resources\Json\JsonResource;
/**
* @mixin SubscriptionChild
*/
class SubscriptionChildResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
*
* @return array{amount: int, name: string}
*/
public function toArray($request)
{
return [
'amount' => $this->amount,
'name' => $this->name,
];
}
}

View File

@ -29,20 +29,29 @@ class SubscriptionController extends Controller
return \Inertia::render('subscription/SubscriptionForm', [ return \Inertia::render('subscription/SubscriptionForm', [
'fees' => Fee::pluck('name', 'id'), 'fees' => Fee::pluck('name', 'id'),
'mode' => 'create', 'mode' => 'create',
'data' => (object) [], 'data' => [
'children' => [],
],
]); ]);
} }
public function store(Request $request): RedirectResponse public function store(Request $request): RedirectResponse
{ {
Subscription::create($request->validate([ $subscriptionParams = $request->validate([
'name' => 'required|max:255', 'name' => 'required|max:255',
'amount' => 'required|numeric',
'fee_id' => 'required|exists:fees,id', 'fee_id' => 'required|exists:fees,id',
], [], [ ], [], [
'fee_id' => 'Nami-Beitrag', 'fee_id' => 'Nami-Beitrag',
'amount' => 'Interner Beitrag', ]);
]));
$children = $request->validate([
'children' => 'present|array',
'children.*.amount' => 'required|numeric',
'children.*.name' => 'required|max:255',
]);
$subscription = Subscription::create($subscriptionParams);
$subscription->children()->createMany($children['children']);
return redirect()->route('subscription.index'); return redirect()->route('subscription.index');
} }
@ -61,14 +70,27 @@ class SubscriptionController extends Controller
public function update(Subscription $subscription, Request $request): RedirectResponse public function update(Subscription $subscription, Request $request): RedirectResponse
{ {
$subscription->update($request->validate([ $subscriptionParams = $request->validate([
'name' => 'required|max:255', 'name' => 'required|max:255',
'amount' => 'required|numeric',
'fee_id' => 'required|exists:fees,id', 'fee_id' => 'required|exists:fees,id',
], [], [ ], [], [
'fee_id' => 'Nami-Beitrag', 'fee_id' => 'Nami-Beitrag',
'amount' => 'Interner Beitrag', ]);
])); $subscription->update($subscriptionParams);
$children = $request->validate([
'children' => 'present|array',
'children.*.amount' => 'required|numeric',
'children.*.name' => 'required|max:255',
]);
$subscription->children()->delete();
$subscription->children()->createMany($children['children']);
return redirect()->route('subscription.index');
}
public function destroy(Subscription $subscription): RedirectResponse
{
$subscription->delete();
return redirect()->route('subscription.index'); return redirect()->route('subscription.index');
} }

View File

@ -23,8 +23,9 @@ class SubscriptionResource extends JsonResource
'name' => $this->name, 'name' => $this->name,
'fee_id' => $this->fee_id, 'fee_id' => $this->fee_id,
'fee_name' => $this->fee->name, 'fee_name' => $this->fee->name,
'amount_human' => number_format($this->amount / 100, 2, ',', '.').' €', 'amount_human' => number_format($this->getAmount() / 100, 2, ',', '.').' €',
'amount' => $this->amount, 'amount' => $this->getAmount(),
'children' => SubscriptionChildResource::collection($this->whenLoaded('children')),
]; ];
} }
} }

View File

@ -7,6 +7,7 @@ use App\Payment\Payment;
use App\Payment\Status; use App\Payment\Status;
use App\Payment\Subscription; use App\Payment\Subscription;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Tests\RequestFactories\Child;
class PaymentFactory extends Factory class PaymentFactory extends Factory
{ {
@ -37,10 +38,13 @@ class PaymentFactory extends Factory
return $this->state(['nr' => $nr]); return $this->state(['nr' => $nr]);
} }
public function subscription(string $name, int $amount): self /**
* @param array<int, Child> $children
*/
public function subscription(string $name, array $children): self
{ {
return $this->for( return $this->for(
Subscription::factory()->state(['name' => $name, 'amount' => $amount])->for(Fee::factory()) Subscription::factory()->state(['name' => $name])->for(Fee::factory())->children($children)
); );
} }
} }

View File

@ -0,0 +1,27 @@
<?php
namespace Database\Factories\Payment;
use App\Payment\SubscriptionChild;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<SubscriptionChild>
*/
class SubscriptionChildFactory extends Factory
{
protected $model = SubscriptionChild::class;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
return [
'amount' => $this->faker->numberBetween(10, 3000),
'name' => $this->faker->words(5, true),
];
}
}

View File

@ -4,7 +4,9 @@ namespace Database\Factories\Payment;
use App\Fee; use App\Fee;
use App\Payment\Subscription; use App\Payment\Subscription;
use App\Payment\SubscriptionChild;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Tests\RequestFactories\Child;
class SubscriptionFactory extends Factory class SubscriptionFactory extends Factory
{ {
@ -14,7 +16,6 @@ class SubscriptionFactory extends Factory
{ {
return [ return [
'name' => $this->faker->word, 'name' => $this->faker->word,
'amount' => $this->faker->numberBetween(1000, 50000),
'fee_id' => Fee::factory()->createOne()->id, 'fee_id' => Fee::factory()->createOne()->id,
]; ];
} }
@ -24,8 +25,17 @@ class SubscriptionFactory extends Factory
return $this->state(['name' => $name]); return $this->state(['name' => $name]);
} }
public function amount(int $amount): self /**
* @param array<int, Child> $children
*/
public function children(array $children): self
{ {
return $this->state(['amount' => $amount]); $instance = $this;
foreach ($children as $child) {
$instance = $instance->has(SubscriptionChild::factory()->state($child->toArray()), 'children');
}
return $instance;
} }
} }

View File

@ -0,0 +1,46 @@
<?php
use App\Payment\SubscriptionChild;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class() extends Migration {
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$subscriptions = DB::table('subscriptions')->get();
Schema::table('subscriptions', function (Blueprint $table) {
$table->dropColumn('amount');
});
Schema::create('subscription_children', function (Blueprint $table) {
$table->uuid('id');
$table->foreignId('parent_id')->constrained('subscriptions');
$table->string('name');
$table->unsignedInteger('amount');
});
foreach ($subscriptions as $subscription) {
SubscriptionChild::create([
'parent_id' => $subscription->id,
'name' => 'name',
'amount' => $subscription->amount,
]);
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
};

View File

@ -1,16 +1,50 @@
<template> <template>
<form id="subedit" class="p-6 grid gap-4 justify-start" @submit.prevent="submit"> <form id="subedit" class="p-3 grid gap-3" @submit.prevent="submit">
<save-button form="subedit"></save-button> <save-button form="subedit"></save-button>
<f-text id="name" v-model="inner.name" label="Name" required></f-text> <box heading="Beitrag">
<f-select <div class="grid gap-4 sm:grid-cols-2">
id="fee_id" <f-text id="name" v-model="inner.name" label="Name" size="sm" required></f-text>
name="fee_id" <f-select
:options="fees" id="fee_id"
v-model="inner.fee_id" name="fee_id"
label="Nami-Beitrag" :options="fees"
required v-model="inner.fee_id"
></f-select> label="Nami-Beitrag"
<f-text id="amount" v-model="inner.amount" label="Interner Beitrag" mode="area" required></f-text> size="sm"
required
></f-select>
</div>
</box>
<box heading="Positionen">
<div class="flex flex-col space-y-4">
<div v-for="(pos, index) in inner.children" :key="index" class="flex space-x-2 items-end">
<f-text :id="`name-${index}`" v-model="pos.name" label="Name" size="sm" required></f-text>
<f-text
:id="`amount-${index}`"
v-model="pos.amount"
label="Beitrag"
size="sm"
mode="area"
required
></f-text>
<a
href="#"
@click.prevent="inner.children.splice(index, 1)"
class="btn btn-sm btn-danger icon flex-none"
>
<svg-sprite src="trash" class="w-5 h-5"></svg-sprite>
</a>
</div>
<a
href="#"
@click.prevent="inner.children.push({name: '', amount: 0})"
class="btn btn-sm flex btn-primary flex self-start mt-4"
>
<svg-sprite src="plus" class="w-5 h-5"></svg-sprite>
Position hinzufügen
</a>
</div>
</box>
</form> </form>
</template> </template>

View File

@ -11,6 +11,7 @@ use App\Letter\RememberDocument;
use App\Member\Member; use App\Member\Member;
use App\Payment\Payment; use App\Payment\Payment;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
use Zoomyboy\Tex\Tex; use Zoomyboy\Tex\Tex;
@ -40,7 +41,10 @@ class DocumentFactoryTest extends TestCase
'zip' => '::zip::', 'zip' => '::zip::',
'location' => '::location::', 'location' => '::location::',
]) ])
->has(Payment::factory()->notPaid()->nr('1995')->subscription('::subName::', 1500)) ->has(Payment::factory()->notPaid()->nr('1995')->subscription('::subName::', [
new Child('a', 1000),
new Child('a', 500),
]))
->create(); ->create();
$letter = app(DocumentFactory::class)->singleLetter(BillDocument::class, $this->query($member)); $letter = app(DocumentFactory::class)->singleLetter(BillDocument::class, $this->query($member));
@ -59,7 +63,7 @@ class DocumentFactoryTest extends TestCase
$member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->state(['lastname' => '::lastname::']) ->state(['lastname' => '::lastname::'])
->has(Payment::factory()->notPaid()->nr('1995')->subscription('::subName::', 1500)) ->has(Payment::factory()->notPaid()->nr('1995'))
->create(); ->create();
$letter = app(DocumentFactory::class)->singleLetter(BillDocument::class, $this->query($member)); $letter = app(DocumentFactory::class)->singleLetter(BillDocument::class, $this->query($member));

View File

@ -11,6 +11,7 @@ use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Mail; use Mail;
use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
use Zoomyboy\Tex\Tex; use Zoomyboy\Tex\Tex;
@ -29,7 +30,9 @@ class LetterSendActionTest extends TestCase
$this->login()->loginNami(); $this->login()->loginNami();
$this->member = Member::factory() $this->member = Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [
new Child('a', 5400),
]))
->emailBillKind() ->emailBillKind()
->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']); ->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']);
} }

View File

@ -9,6 +9,7 @@ use App\Payment\Payment;
use App\Subactivity; use App\Subactivity;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
class IndexTest extends TestCase class IndexTest extends TestCase
@ -108,7 +109,10 @@ class IndexTest extends TestCase
{ {
$this->withoutExceptionHandling()->login()->loginNami(); $this->withoutExceptionHandling()->login()->loginNami();
$member = Member::factory() $member = Member::factory()
->has(Payment::factory()->notPaid()->nr('2019')->subscription('Free', 1050)) ->has(Payment::factory()->notPaid()->nr('2019')->subscription('Free', [
new Child('a', 1000),
new Child('b', 50),
]))
->defaults()->create(); ->defaults()->create();
$response = $this->get('/member'); $response = $this->get('/member');

View File

@ -15,6 +15,7 @@ use App\Payment\Subscription;
use App\Region; use App\Region;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
class ShowTest extends TestCase class ShowTest extends TestCase
@ -34,7 +35,10 @@ class ShowTest extends TestCase
$member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->has(Membership::factory()->promise(now())->in('€ LeiterIn', 5, 'Jungpfadfinder', 88)->state(['created_at' => '2022-11-19 05:00:00'])) ->has(Membership::factory()->promise(now())->in('€ LeiterIn', 5, 'Jungpfadfinder', 88)->state(['created_at' => '2022-11-19 05:00:00']))
->has(Payment::factory()->notPaid()->nr('2019')->subscription('Free', 1050)) ->has(Payment::factory()->notPaid()->nr('2019')->subscription('Free', [
new Child('uu', 1000),
new Child('a', 50),
]))
->for(Gender::factory()->name('Männlich')) ->for(Gender::factory()->name('Männlich'))
->for(Region::factory()->name('NRW')) ->for(Region::factory()->name('NRW'))
->postBillKind() ->postBillKind()

View File

@ -6,6 +6,7 @@ use App\Member\Member;
use App\Payment\MemberPaymentBlock; use App\Payment\MemberPaymentBlock;
use App\Payment\Payment; use App\Payment\Payment;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
class MemberPaymentsBlockTest extends TestCase class MemberPaymentsBlockTest extends TestCase
@ -18,7 +19,10 @@ class MemberPaymentsBlockTest extends TestCase
Member::factory() Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->subscription('example', 3400)) ->has(Payment::factory()->notPaid()->subscription('example', [
new Child('gg', 3400),
new Child('gg', 100),
]))
->create(); ->create();
Member::factory() Member::factory()
->defaults() ->defaults()
@ -27,7 +31,7 @@ class MemberPaymentsBlockTest extends TestCase
$data = app(MemberPaymentBlock::class)->render()['data']; $data = app(MemberPaymentBlock::class)->render()['data'];
$this->assertEquals([ $this->assertEquals([
'amount' => '34,00 €', 'amount' => '35,00 €',
'members' => 1, 'members' => 1,
'total_members' => 2, 'total_members' => 2,
], $data); ], $data);

View File

@ -8,6 +8,7 @@ use App\Member\Member;
use App\Payment\Payment; use App\Payment\Payment;
use App\Payment\Status; use App\Payment\Status;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\RequestFactories\LetterSettingsFake; use Tests\RequestFactories\LetterSettingsFake;
use Tests\TestCase; use Tests\TestCase;
use Zoomyboy\Tex\Tex; use Zoomyboy\Tex\Tex;
@ -37,8 +38,8 @@ class SendpaymentTest extends TestCase
$this->login()->loginNami(); $this->login()->loginNami();
$member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('a', 5400)]))
->has(Payment::factory()->paid()->nr('1998')->subscription('bezahltdesc', 5800)) ->has(Payment::factory()->paid()->nr('1998')->subscription('bezahltdesc', [new Child('b', 5800)]))
->postBillKind() ->postBillKind()
->create(); ->create();
@ -59,7 +60,7 @@ class SendpaymentTest extends TestCase
$this->login()->loginNami(); $this->login()->loginNami();
$member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('u', 5400)]))
->emailBillKind() ->emailBillKind()
->create(); ->create();

View File

@ -0,0 +1,29 @@
<?php
namespace Tests\Feature\Subscription;
use App\Fee;
use App\Payment\Subscription;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase;
class DestroyTest extends TestCase
{
use DatabaseTransactions;
public function testItDeletesChildrenWithSubscription(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->children([
new Child('a', 1400),
new Child('b', 1500),
])->create();
$response = $this->from('/subscription')->delete("/subscription/{$subscription->id}");
$response->assertRedirect('/subscription');
$this->assertDatabaseCount('subscription_children', 0);
$this->assertDatabaseCount('subscriptions', 0);
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Tests\Feature\Subscription;
use App\Fee;
use App\Payment\Subscription;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\TestCase;
class EditTest extends TestCase
{
use DatabaseTransactions;
public function testItReturnsChildren(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->children([
new Child('a', 1400),
new Child('b', 1500),
])->create();
$response = $this->get("/subscription/{$subscription->id}/edit");
$this->assertInertiaHas([
'children' => [
['name' => 'a', 'amount' => 1400],
['name' => 'b', 'amount' => 1500],
],
'name' => 'hi',
'id' => $subscription->id,
], $response, 'data');
}
}

View File

@ -3,7 +3,9 @@
namespace Tests\Feature\Subscription; namespace Tests\Feature\Subscription;
use App\Fee; use App\Fee;
use App\Payment\Subscription;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\RequestFactories\SubscriptionRequestFactory; use Tests\RequestFactories\SubscriptionRequestFactory;
use Tests\TestCase; use Tests\TestCase;
@ -18,14 +20,21 @@ class StoreTest extends TestCase
$response = $this->from('/subscription')->post( $response = $this->from('/subscription')->post(
'/subscription', '/subscription',
SubscriptionRequestFactory::new()->amount(2500)->fee($fee)->name('lorem')->create() SubscriptionRequestFactory::new()->fee($fee)->name('lorem')->children([
new Child('ch', 2500),
])->create()
); );
$response->assertRedirect('/subscription'); $response->assertRedirect('/subscription');
$subscription = Subscription::firstWhere('name', 'lorem');
$this->assertDatabaseHas('subscriptions', [ $this->assertDatabaseHas('subscriptions', [
'amount' => 2500,
'fee_id' => $fee->id, 'fee_id' => $fee->id,
'name' => 'Lorem', 'name' => 'lorem',
]);
$this->assertDatabaseHas('subscription_children', [
'name' => 'ch',
'amount' => 2500,
'parent_id' => $subscription->id,
]); ]);
} }
@ -40,7 +49,6 @@ class StoreTest extends TestCase
); );
$this->assertErrors([ $this->assertErrors([
'amount' => 'Interner Beitrag ist erforderlich.',
'fee_id' => 'Nami-Beitrag ist nicht vorhanden.', 'fee_id' => 'Nami-Beitrag ist nicht vorhanden.',
'name' => 'Name ist erforderlich.', 'name' => 'Name ist erforderlich.',
], $response); ], $response);

View File

@ -5,6 +5,7 @@ namespace Tests\Feature\Subscription;
use App\Fee; use App\Fee;
use App\Payment\Subscription; use App\Payment\Subscription;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\RequestFactories\Child;
use Tests\RequestFactories\SubscriptionRequestFactory; use Tests\RequestFactories\SubscriptionRequestFactory;
use Tests\TestCase; use Tests\TestCase;
@ -15,7 +16,7 @@ class UpdateTest extends TestCase
public function testItUpdatesASubscription(): void public function testItUpdatesASubscription(): void
{ {
$this->withoutExceptionHandling()->login()->loginNami(); $this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->amount(670)->name('hi')->for(Fee::factory())->create(); $subscription = Subscription::factory()->name('hi')->for(Fee::factory())->create();
$fee = Fee::factory()->create(); $fee = Fee::factory()->create();
$response = $this->from("/subscription/{$subscription->id}")->patch( $response = $this->from("/subscription/{$subscription->id}")->patch(
@ -26,17 +27,39 @@ class UpdateTest extends TestCase
$response->assertRedirect('/subscription'); $response->assertRedirect('/subscription');
$this->assertDatabaseHas('subscriptions', [ $this->assertDatabaseHas('subscriptions', [
'id' => $subscription->id, 'id' => $subscription->id,
'amount' => 2500,
'fee_id' => $fee->id, 'fee_id' => $fee->id,
'name' => 'Lorem', 'name' => 'Lorem',
]); ]);
} }
public function testItUpdatesChildren(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$subscription = Subscription::factory()->name('hi')->for(Fee::factory())->children([
new Child('a', 1400),
new Child('b', 1500),
])->create();
$response = $this->from("/subscription/{$subscription->id}")->patch(
"/subscription/{$subscription->id}",
SubscriptionRequestFactory::new()->children([
new Child('c', 1900),
])->create()
);
$response->assertRedirect('/subscription');
$this->assertDatabaseHas('subscription_children', [
'parent_id' => $subscription->id,
'name' => 'c',
'amount' => 1900,
]);
$this->assertDatabaseCount('subscription_children', 1);
}
public function testItValidatesRequest(): void public function testItValidatesRequest(): void
{ {
$this->login()->loginNami(); $this->login()->loginNami();
$subscription = Subscription::factory()->amount(670)->name('hi')->for(Fee::factory())->create(); $subscription = Subscription::factory()->name('hi')->for(Fee::factory())->create();
$fee = Fee::factory()->create();
$response = $this->from("/subscription/{$subscription->id}")->patch( $response = $this->from("/subscription/{$subscription->id}")->patch(
"/subscription/{$subscription->id}", "/subscription/{$subscription->id}",
@ -44,7 +67,6 @@ class UpdateTest extends TestCase
); );
$this->assertErrors([ $this->assertErrors([
'amount' => 'Interner Beitrag ist erforderlich.',
'fee_id' => 'Nami-Beitrag ist nicht vorhanden.', 'fee_id' => 'Nami-Beitrag ist nicht vorhanden.',
'name' => 'Name ist erforderlich.', 'name' => 'Name ist erforderlich.',
], $response); ], $response);

View File

@ -0,0 +1,21 @@
<?php
namespace Tests\RequestFactories;
class Child
{
public function __construct(private string $name, private int $amount)
{
}
/**
* @return array{name: string, amount: int}
*/
public function toArray(): array
{
return [
'amount' => $this->amount,
'name' => $this->name,
];
}
}

View File

@ -7,12 +7,15 @@ use Worksome\RequestFactories\RequestFactory;
class SubscriptionRequestFactory extends RequestFactory class SubscriptionRequestFactory extends RequestFactory
{ {
/**
* @return array<string, int|string|array<int, array{amount: int, name: string}>>
*/
public function definition(): array public function definition(): array
{ {
return [ return [
'amount' => $this->faker->numberBetween(100, 2000),
'fee_id' => Fee::factory()->create()->id, 'fee_id' => Fee::factory()->create()->id,
'name' => $this->faker->words(5, true), 'name' => $this->faker->words(5, true),
'children' => [],
]; ];
} }
@ -34,9 +37,16 @@ class SubscriptionRequestFactory extends RequestFactory
public function invalid(): self public function invalid(): self
{ {
return $this->state([ return $this->state([
'amount' => '',
'fee_id' => 9999, 'fee_id' => 9999,
'name' => '', 'name' => '',
]); ]);
} }
/**
* @param array<int, Child> $children
*/
public function children(array $children): self
{
return $this->state(['children' => array_map(fn ($child) => $child->toArray(), $children)]);
}
} }