diff --git a/app/Http/Views/MemberView.php b/app/Http/Views/MemberView.php index 69b391f5..22f9fe39 100644 --- a/app/Http/Views/MemberView.php +++ b/app/Http/Views/MemberView.php @@ -4,6 +4,7 @@ namespace App\Http\Views; use App\Member\Member; use App\Member\MemberResource; +use App\Payment\ActionFactory; use App\Payment\PaymentResource; use App\Payment\Status; use App\Payment\Subscription; @@ -43,6 +44,7 @@ class MemberView { 'links' => [ ['icon' => 'plus', 'href' => route('member.payment.create', ['member' => $member]) ], ], + 'payment_links' => app(ActionFactory::class)->forMember($member), 'mode' => 'index', ]); } diff --git a/app/Member/MemberResource.php b/app/Member/MemberResource.php index ec796be7..64255015 100644 --- a/app/Member/MemberResource.php +++ b/app/Member/MemberResource.php @@ -2,8 +2,8 @@ namespace App\Member; -use Illuminate\Http\Resources\Json\JsonResource; use App\Payment\PaymentResource; +use Illuminate\Http\Resources\Json\JsonResource; class MemberResource extends JsonResource { diff --git a/app/Payment/ActionFactory.php b/app/Payment/ActionFactory.php new file mode 100644 index 00000000..e83b6067 --- /dev/null +++ b/app/Payment/ActionFactory.php @@ -0,0 +1,25 @@ +<?php + +namespace App\Payment; + +use App\Member\Member; +use App\Pdf\PdfRepositoryFactory; +use Illuminate\Support\Collection; + +class ActionFactory +{ + + public function forMember(Member $member): Collection + { + return app(PdfRepositoryFactory::class)->getTypes()->map(function(string $repo) use ($member) { + $repo = app($repo); + + return [ + 'href' => route('member.singlepdf', ['member' => $member, 'type' => get_class($repo)]), + 'label' => $repo->linkLabel(), + 'disabled' => !$repo->createable($member), + ]; + }); + } + +} diff --git a/app/Pdf/BillType.php b/app/Pdf/BillType.php index feaf1a2a..c4986ad2 100644 --- a/app/Pdf/BillType.php +++ b/app/Pdf/BillType.php @@ -2,6 +2,7 @@ namespace App\Pdf; +use App\Member\Member; use App\Payment\Payment; use Illuminate\Support\Collection; @@ -16,6 +17,16 @@ class BillType extends Repository implements PdfRepository $this->pages = $pages; } + public function createable(Member $member): bool + { + return $member->payments()->whereNeedsBill()->count() !== 0; + } + + public function linkLabel(): string + { + return 'Rechnung erstellen'; + } + public function getSubject(): string { return 'Rechnung'; diff --git a/app/Pdf/PdfRepository.php b/app/Pdf/PdfRepository.php index c3ba4e4e..b3de60bc 100644 --- a/app/Pdf/PdfRepository.php +++ b/app/Pdf/PdfRepository.php @@ -2,6 +2,7 @@ namespace App\Pdf; +use App\Member\Member; use Illuminate\Support\Collection; interface PdfRepository @@ -27,4 +28,8 @@ interface PdfRepository public function getLocation(Collection $page): string; + public function createable(Member $member): bool; + + public function linkLabel(): string; + } diff --git a/app/Pdf/PdfRepositoryFactory.php b/app/Pdf/PdfRepositoryFactory.php index f1c63fa9..695a3cd2 100644 --- a/app/Pdf/PdfRepositoryFactory.php +++ b/app/Pdf/PdfRepositoryFactory.php @@ -9,6 +9,15 @@ use Illuminate\Support\Str; class PdfRepositoryFactory { + private array $types = [ + BillType::class, + ]; + + public function getTypes(): Collection + { + return collect($this->types); + } + public function fromSingleRequest(string $type, Member $member): ?PdfRepository { $members = $this->singleMemberCollection($member); diff --git a/database/factories/Payment/PaymentFactory.php b/database/factories/Payment/PaymentFactory.php index 81fb280e..ab13b8e7 100644 --- a/database/factories/Payment/PaymentFactory.php +++ b/database/factories/Payment/PaymentFactory.php @@ -25,6 +25,11 @@ class PaymentFactory extends Factory return $this->for(Status::whereName('Nicht bezahlt')->first()); } + public function paid(): self + { + return $this->for(Status::whereName('Rechnung beglichen')->first()); + } + public function nr(string $nr): self { return $this->state(['nr' => $nr]); diff --git a/resources/js/views/member/Payments.vue b/resources/js/views/member/Payments.vue index c3af20e8..fedc51c3 100644 --- a/resources/js/views/member/Payments.vue +++ b/resources/js/views/member/Payments.vue @@ -22,8 +22,7 @@ </div> </div> <div class="flex flex-col pb-6 px-6"> - <a href="#" class="text-center btn btn-primary">Rechnung erstellen</a> - <a href="#" class="text-center mt-1 btn btn-primary">Erinnerung erstellen</a> + <a href="#" @click.prevent="openLink(link)" :class="{'disabled': link.disabled}" target="_BLANK" v-for="link in value.payment_links" class="mt-1 text-center btn btn-primary" v-text="link.label"></a> </div> </div> </template> @@ -41,6 +40,14 @@ export default { accept(payment) { this.$inertia.patch(`/member/${this.value.data.id}/payment/${payment.id}`, { ...payment, status_id: 3 }); + }, + + openLink(link) { + if (link.disabled) { + return; + } + + window.open(link.href); } }, diff --git a/tests/Feature/Payments/IndexTest.php b/tests/Feature/Payments/IndexTest.php new file mode 100644 index 00000000..1f66614a --- /dev/null +++ b/tests/Feature/Payments/IndexTest.php @@ -0,0 +1,70 @@ +<?php + +namespace Tests\Feature\Payments; + +use App\Member\Member; +use App\Pdf\BillType; +use Database\Factories\Payment\PaymentFactory; +use Illuminate\Foundation\Testing\RefreshDatabase; +use Tests\TestCase; + +class IndexTest extends TestCase +{ + + use RefreshDatabase; + + public function testItGetsLinkWhenMemberHasPayableBills(): void + { + $this->withoutExceptionHandling(); + $this->login(); + + $member = Member::factory() + ->defaults() + ->withPayments([ + fn (PaymentFactory $payment) => $payment->notPaid()->nr('1995')->subscription('::subName::', 1500), + ]) + ->create(['firstname' => '::firstname']); + + $this->get("/member/{$member->id}/payment")->assertInertia('member/Index', [ + 'href' => route('member.singlepdf', ['member' => $member, 'type' => BillType::class]), + 'label' => 'Rechnung erstellen', + 'disabled' => false, + ], 'single.payment_links.0'); + } + + public function testLinkIsFalseWhenMemberHasnoPayments(): void + { + $this->withoutExceptionHandling(); + $this->login(); + + $member = Member::factory() + ->defaults() + ->create(['firstname' => '::firstname']); + + $this->get("/member/{$member->id}/payment")->assertInertia('member/Index', [ + 'href' => route('member.singlepdf', ['member' => $member, 'type' => BillType::class]), + 'label' => 'Rechnung erstellen', + 'disabled' => true, + ], 'single.payment_links.0'); + } + + public function testItReturnsDisabledWhenPaymentsArePaid(): void + { + $this->withoutExceptionHandling(); + $this->login(); + + $member = Member::factory() + ->defaults() + ->withPayments([ + fn (PaymentFactory $payment) => $payment->paid()->nr('1995')->subscription('::subName::', 1500), + ]) + ->create(['firstname' => '::firstname']); + + $this->get("/member/{$member->id}/payment")->assertInertia('member/Index', [ + 'href' => route('member.singlepdf', ['member' => $member, 'type' => BillType::class]), + 'label' => 'Rechnung erstellen', + 'disabled' => true, + ], 'single.payment_links.0'); + } + +}