From a8560633800ec90145f95652681dffe72a9b18f3 Mon Sep 17 00:00:00 2001 From: Philipp Lang <philipp@aweos.de> Date: Mon, 7 Nov 2022 16:18:11 +0100 Subject: [PATCH] Fixed tests --- app/Console/Kernel.php | 2 - app/Contribution/ContributionController.php | 10 +- .../{DvData.php => DvDocument.php} | 47 +++--- ...{SolingenData.php => SolingenDocument.php} | 80 +++++----- app/Http/Views/MemberView.php | 33 ---- app/Letter/Actions/LetterSendAction.php | 49 ++++++ app/Letter/BillDocument.php | 62 ++++++++ app/{Bill => Letter}/BillKind.php | 2 +- app/Letter/DocumentFactory.php | 149 ++++++++++++++++++ app/Letter/Letter.php | 108 +++++++++++++ .../LetterSettings.php} | 4 +- app/Letter/MailRecipient.php | 15 ++ app/Letter/Page.php | 68 ++++++++ .../RememberDocument.php} | 44 ++---- app/{Bill => Letter}/SettingIndexAction.php | 6 +- app/{Bill => Letter}/SettingSaveAction.php | 4 +- app/Member/Member.php | 2 +- app/Member/MemberController.php | 2 +- app/Payment/ActionFactory.php | 20 +-- app/Payment/PaymentController.php | 24 --- app/Payment/PaymentMail.php | 14 +- app/Payment/PaymentSendCommand.php | 80 ---------- app/Payment/SendpaymentController.php | 18 ++- app/Pdf/BillType.php | 125 --------------- app/Pdf/Data/MemberEfzData.php | 68 -------- app/Pdf/EnvType.php | 9 -- app/Pdf/LetterRepository.php | 46 ------ app/Pdf/MemberPdfController.php | 8 +- app/Pdf/PdfGenerator.php | 67 -------- app/Pdf/PdfRepository.php | 16 -- app/Pdf/PdfRepositoryFactory.php | 110 ------------- app/Pdf/Repository.php | 46 ------ app/Setting/SettingServiceProvider.php | 4 +- .../{Bill => Letter}/BillKindFactory.php | 4 +- database/factories/Member/MemberFactory.php | 15 ++ ...2020_04_12_223230_create_members_table.php | 2 +- packages/tex | 2 +- ...type.blade.php => bill_document.blade.php} | 2 +- ....blade.php => remember_document.blade.php} | 2 +- resources/views/tex/bill.tex | 14 +- resources/views/tex/remember.tex | 14 +- .../teilnahmeliste.pdf | Bin resources/views/tex/zuschuss-dv.tex | 18 +-- resources/views/tex/zuschuss-stadt.tex | 10 +- routes/web.php | 2 +- tests/Feature/Bill/SettingTest.php | 6 +- tests/Feature/ContributionTest.php | 44 ++++++ tests/Feature/Letter/LetterSendActionTest.php | 56 +++++++ tests/Feature/Member/StoreTest.php | 2 +- tests/Feature/Pdf/GenerateTest.php | 35 ++-- tests/Feature/Sendpayment/SendpaymentTest.php | 68 ++++++++ 51 files changed, 811 insertions(+), 827 deletions(-) rename app/Contribution/{DvData.php => DvDocument.php} (77%) rename app/Contribution/{SolingenData.php => SolingenDocument.php} (67%) create mode 100644 app/Letter/Actions/LetterSendAction.php create mode 100644 app/Letter/BillDocument.php rename app/{Bill => Letter}/BillKind.php (91%) create mode 100644 app/Letter/DocumentFactory.php create mode 100644 app/Letter/Letter.php rename app/{Bill/BillSettings.php => Letter/LetterSettings.php} (91%) create mode 100644 app/Letter/MailRecipient.php create mode 100644 app/Letter/Page.php rename app/{Pdf/RememberType.php => Letter/RememberDocument.php} (77%) rename app/{Bill => Letter}/SettingIndexAction.php (84%) rename app/{Bill => Letter}/SettingSaveAction.php (93%) delete mode 100644 app/Payment/PaymentSendCommand.php delete mode 100644 app/Pdf/BillType.php delete mode 100644 app/Pdf/Data/MemberEfzData.php delete mode 100644 app/Pdf/EnvType.php delete mode 100644 app/Pdf/LetterRepository.php delete mode 100644 app/Pdf/PdfGenerator.php delete mode 100644 app/Pdf/PdfRepository.php delete mode 100644 app/Pdf/PdfRepositoryFactory.php delete mode 100644 app/Pdf/Repository.php rename database/factories/{Bill => Letter}/BillKindFactory.php (87%) rename resources/views/mail/payment/{bill_type.blade.php => bill_document.blade.php} (80%) rename resources/views/mail/payment/{remember_type.blade.php => remember_document.blade.php} (82%) rename resources/views/tex/templates/{zuschussdv => contribution}/teilnahmeliste.pdf (100%) create mode 100644 tests/Feature/ContributionTest.php create mode 100644 tests/Feature/Letter/LetterSendActionTest.php create mode 100644 tests/Feature/Sendpayment/SendpaymentTest.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 71120fcf..c7dc3746 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -2,7 +2,6 @@ namespace App\Console; -use App\Payment\PaymentSendCommand; use Illuminate\Console\Scheduling\Schedule; use Illuminate\Foundation\Console\Kernel as ConsoleKernel; @@ -14,7 +13,6 @@ class Kernel extends ConsoleKernel * @var array */ protected $commands = [ - PaymentSendCommand::class, ]; /** diff --git a/app/Contribution/ContributionController.php b/app/Contribution/ContributionController.php index a4cbff8c..b1eb2da9 100644 --- a/app/Contribution/ContributionController.php +++ b/app/Contribution/ContributionController.php @@ -6,10 +6,11 @@ use App\Country; use App\Http\Controllers\Controller; use App\Member\Member; use App\Member\MemberResource; -use App\Pdf\PdfGenerator; use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; +use Zoomyboy\Tex\BaseCompiler; +use Zoomyboy\Tex\Tex; class ContributionController extends Controller { @@ -25,10 +26,11 @@ class ContributionController extends Controller ]); } - public function generate(Request $request): PdfGenerator + public function generate(Request $request): BaseCompiler { - $data = app($request->query('type')); + /** @var class-string<SolingenDocument> */ + $type = $request->query('type'); - return app(PdfGenerator::class)->setRepository($data)->render(); + return Tex::compile($type::fromRequest($request)); } } diff --git a/app/Contribution/DvData.php b/app/Contribution/DvDocument.php similarity index 77% rename from app/Contribution/DvData.php rename to app/Contribution/DvDocument.php index c38f1298..04a2c814 100644 --- a/app/Contribution/DvData.php +++ b/app/Contribution/DvDocument.php @@ -4,26 +4,33 @@ namespace App\Contribution; use App\Country; use App\Member\Member; -use App\Pdf\EnvType; -use App\Pdf\PdfRepository; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection; use Illuminate\Http\Request; -use Spatie\LaravelData\Data; +use Zoomyboy\Tex\Document; +use Zoomyboy\Tex\Engine; +use Zoomyboy\Tex\Template; -class DvData extends Data implements PdfRepository +class DvDocument extends Document { public function __construct( public string $dateFrom, public string $dateUntil, public string $zipLocation, public ?Country $country, - public array $members, + public Collection $members, public ?string $filename = '', public string $type = 'F', ) { } + public function dateRange(): string + { + return Carbon::parse($this->dateFrom)->format('d.m.Y') + .' - ' + .Carbon::parse($this->dateUntil)->format('d.m.Y'); + } + public static function fromRequest(Request $request): self { return new self( @@ -31,13 +38,13 @@ class DvData extends Data implements PdfRepository dateUntil: $request->dateUntil, zipLocation: $request->zipLocation, country: Country::findOrFail($request->country), - members: $request->members, + members: Member::whereIn('id', $request->members)->orderByRaw('lastname, firstname')->get()->chunk(17), ); } - public function members(): Collection + public function countryName(): string { - return Member::whereIn('id', $this->members)->orderByRaw('lastname, firstname')->get(); + return $this->country->name; } public function memberShort(Member $member): string @@ -69,31 +76,19 @@ class DvData extends Data implements PdfRepository return (string) $member->getAge(); } - public function countryName(): string - { - return $this->country->name; - } - - public function dateRange(): string - { - return Carbon::parse($this->dateFrom)->format('d.m.Y') - .' - ' - .Carbon::parse($this->dateUntil)->format('d.m.Y'); - } - - public function getFilename(): string + public function basename(): string { return 'zuschuesse-dv'; } - public function getView(): string + public function view(): string { return 'tex.zuschuss-dv'; } - public function getTemplate(): ?string + public function template(): Template { - return 'zuschussdv'; + return Template::make('tex.templates.contribution'); } public function setFilename(string $filename): static @@ -103,8 +98,8 @@ class DvData extends Data implements PdfRepository return $this; } - public function getScript(): EnvType + public function getEngine(): Engine { - return EnvType::PDFLATEX; + return Engine::PDFLATEX; } } diff --git a/app/Contribution/SolingenData.php b/app/Contribution/SolingenDocument.php similarity index 67% rename from app/Contribution/SolingenData.php rename to app/Contribution/SolingenDocument.php index e759f66b..53b470a4 100644 --- a/app/Contribution/SolingenData.php +++ b/app/Contribution/SolingenDocument.php @@ -3,36 +3,61 @@ namespace App\Contribution; use App\Member\Member; -use App\Pdf\EnvType; -use App\Pdf\PdfRepository; use Carbon\Carbon; use Illuminate\Database\Eloquent\Collection; use Illuminate\Http\Request; use Illuminate\Support\Str; -use Spatie\LaravelData\Data; +use Zoomyboy\Tex\Document; +use Zoomyboy\Tex\Engine; +use Zoomyboy\Tex\Template; -class SolingenData extends Data implements PdfRepository +class SolingenDocument extends Document { - public function __construct( - public string $eventName, + final private function __construct( public string $dateFrom, public string $dateUntil, + public string $zipLocation, + /** @var array<int, int> */ public array $members, - public ?string $filename = '', + public string $eventName, public string $type = 'F', ) { } - public static function fromRequest(Request $request): self + public static function fromRequest(Request $request): static { - return new self( - eventName: $request->eventName, + return new static( dateFrom: $request->dateFrom, dateUntil: $request->dateUntil, + zipLocation: $request->zipLocation, members: $request->members, + eventName: $request->eventName, ); } + /** + * @return Collection<Collection<Member>> + */ + public function memberModels(): Collection + { + return Member::whereIn('id', $this->members)->orderByRaw('lastname, firstname')->get()->chunk(14); + } + + public function niceEventFrom(): string + { + return Carbon::parse($this->dateFrom)->format('d.m.Y'); + } + + public function niceEventUntil(): string + { + return Carbon::parse($this->dateUntil)->format('d.m.Y'); + } + + public function template(): Template + { + return Template::make('tex.templates.contribution'); + } + public function checkboxes(): string { $output = ''; @@ -48,45 +73,18 @@ class SolingenData extends Data implements PdfRepository return $firstRow."\n".$secondRow; } - public function members(): Collection - { - return Member::whereIn('id', $this->members)->orderByRaw('lastname, firstname')->get(); - } - - public function niceEventFrom(): string - { - return Carbon::parse($this->dateFrom)->format('d.m.Y'); - } - - public function niceEventTo(): string - { - return Carbon::parse($this->dateUntil)->format('d.m.Y'); - } - - public function getFilename(): string + public function basename(): string { return 'zuschuesse-solingen-'.Str::slug($this->eventName); } - public function getView(): string + public function view(): string { return 'tex.zuschuss-stadt'; } - public function getTemplate(): ?string + public function getEngine(): Engine { - return null; - } - - public function setFilename(string $filename): static - { - $this->filename = $filename; - - return $this; - } - - public function getScript(): EnvType - { - return EnvType::PDFLATEX; + return Engine::PDFLATEX; } } diff --git a/app/Http/Views/MemberView.php b/app/Http/Views/MemberView.php index 7bb388f6..052a0381 100644 --- a/app/Http/Views/MemberView.php +++ b/app/Http/Views/MemberView.php @@ -6,9 +6,6 @@ use App\Activity; use App\Course\Models\Course; use App\Member\Member; use App\Member\MemberResource; -use App\Payment\ActionFactory; -use App\Payment\Payment; -use App\Payment\PaymentResource; use App\Payment\Status; use App\Payment\Subscription; use App\Region; @@ -43,34 +40,4 @@ class MemberView })->pluck('subactivities', 'id'), ]; } - - public function paymentEdit(Member $member, Payment $payment): MemberResource - { - return $this->additional($member, [ - 'model' => new PaymentResource($payment), - 'links' => [['label' => 'Zurück', 'href' => route('member.payment.index', ['member' => $member])]], - 'mode' => 'edit', - ]); - } - - public function paymentIndex(Member $member): MemberResource - { - return $this->additional($member, [ - 'model' => null, - 'links' => [ - ['icon' => 'plus', 'href' => route('member.payment.create', ['member' => $member])], - ], - 'payment_links' => app(ActionFactory::class)->forMember($member), - 'mode' => 'index', - ]); - } - - private function additional(Member $member, array $overwrites = []): MemberResource - { - return (new MemberResource($member->load('payments'))) - ->additional(array_merge([ - 'subscriptions' => Subscription::pluck('name', 'id'), - 'statuses' => Status::pluck('name', 'id'), - ], $overwrites)); - } } diff --git a/app/Letter/Actions/LetterSendAction.php b/app/Letter/Actions/LetterSendAction.php new file mode 100644 index 00000000..aaf641b5 --- /dev/null +++ b/app/Letter/Actions/LetterSendAction.php @@ -0,0 +1,49 @@ +<?php + +namespace App\Letter\Actions; + +use App\Letter\DocumentFactory; +use App\Payment\PaymentMail; +use Illuminate\Console\Command; +use Illuminate\Support\Facades\Storage; +use Lorisleiva\Actions\Concerns\AsAction; +use Mail; +use Zoomyboy\Tex\Tex; + +class LetterSendAction +{ + use AsAction; + + /** + * The name and signature of the console command. + * + * @var string + */ + protected $signature = 'payment:send'; + + /** + * The console command description. + * + * @var string + */ + protected $description = 'Sends Bills'; + + /** + * Execute the console command. + */ + private function handle(): int + { + foreach (app(DocumentFactory::class)->types as $type) { + $letters = app(DocumentFactory::class)->repoCollection($type, 'E-Mail'); + + foreach ($letters as $letter) { + $letterPath = Storage::path(Tex::compile($letter)->storeIn('/tmp', 'local')); + Mail::to($letter->getRecipient()) + ->send(new PaymentMail($letter, $letterPath)); + app(DocumentFactory::class)->afterSingle($letter); + } + } + + return 0; + } +} diff --git a/app/Letter/BillDocument.php b/app/Letter/BillDocument.php new file mode 100644 index 00000000..1a53ebbb --- /dev/null +++ b/app/Letter/BillDocument.php @@ -0,0 +1,62 @@ +<?php + +namespace App\Letter; + +use App\Payment\Payment; +use Illuminate\Database\Eloquent\Relations\HasMany; + +class BillDocument extends Letter +{ + public function linkLabel(): string + { + return 'Rechnung erstellen'; + } + + public function getSubject(): string + { + return 'Rechnung'; + } + + public function view(): string + { + return 'tex.bill'; + } + + public function sendAllLabel(): string + { + return 'Rechnungen versenden'; + } + + /** + * Get Descriptions for sendpayment page. + * + * @return array<int, string> + */ + public function getDescription(): array + { + return [ + 'Diese Funktion erstellt ein PDF mit allen noch nicht versendenden Rechnungen bei den Mitgliedern die Post als Versandweg haben.', + 'Die Rechnungen werden automatisch auf "Rechnung gestellt" aktualisiert.', + ]; + } + + public function afterSingle(Payment $payment): void + { + $payment->update(['status_id' => 2]); + } + + public function getMailSubject(): string + { + return 'Jahresrechnung'; + } + + /** + * @param HasMany<Payment> $query + * + * @return HasMany<Payment> + */ + public static function paymentsQuery(HasMany $query): HasMany + { + return $query->whereNeedsBill(); + } +} diff --git a/app/Bill/BillKind.php b/app/Letter/BillKind.php similarity index 91% rename from app/Bill/BillKind.php rename to app/Letter/BillKind.php index daa60022..575b8505 100644 --- a/app/Bill/BillKind.php +++ b/app/Letter/BillKind.php @@ -1,6 +1,6 @@ <?php -namespace App\Bill; +namespace App\Letter; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; diff --git a/app/Letter/DocumentFactory.php b/app/Letter/DocumentFactory.php new file mode 100644 index 00000000..c37e00ab --- /dev/null +++ b/app/Letter/DocumentFactory.php @@ -0,0 +1,149 @@ +<?php + +namespace App\Letter; + +use App\Member\Member; +use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Collection as EloquentCollection; +use Illuminate\Support\Collection; +use Illuminate\Support\Str; + +class DocumentFactory +{ + /** + * @var array<int, class-string<Letter>> + */ + public array $types = [ + BillDocument::class, + RememberDocument::class, + ]; + + /** + * @return Collection<int, Letter> + */ + public function getTypes(): Collection + { + /** @var array<int, Member> */ + $emptyMembers = []; + + return collect(array_map(fn ($classString) => new $classString(collect($emptyMembers)), $this->types)); + } + + /** + * @param class-string<Letter> $type + */ + public function fromSingleRequest(string $type, Member $member): ?Letter + { + $members = $this->singleMemberCollection($member, $type); + + if ($members->isEmpty()) { + return null; + } + + $repo = $this->resolve($type, $members); + $repo->setFilename(Str::slug("{$repo->getSubject()} für {$members->first()->singleName}")); + + return $repo; + } + + /** + * @param class-string<Letter> $type + */ + public function forAll(string $type, string $billKind): ?Letter + { + $members = $this->toPages($this->allMemberCollection($type, $billKind)); + + if ($members->isEmpty()) { + return null; + } + + return $this->resolve($type, $members)->setFilename('alle-rechnungen'); + } + + /** + * @param class-string<Letter> $type + * + * @return Collection<int, Letter> + */ + public function repoCollection(string $type, string $billKind): Collection + { + $pages = $this->toPages($this->allMemberCollection($type, $billKind)); + + return $pages->map(fn ($page) => $this->resolve($type, collect([$page]))); + } + + public function afterSingle(Letter $repo): void + { + foreach ($repo->allPayments() as $payment) { + $repo->afterSingle($payment); + } + } + + /** + * @param class-string<Letter> $type + */ + public function afterAll(string $type, string $billKind): void + { + $members = $this->allMemberCollection($type, $billKind); + $repo = $this->resolve($type, $this->toPages($members)); + + $this->afterSingle($repo); + } + + /** + * @param class-string<Letter> $type + * + * @return Collection<int, Page> + */ + public function singleMemberCollection(Member $member, string $type): Collection + { + $members = Member::where($member->only(['lastname', 'address', 'zip', 'location'])) + ->with([ + 'payments' => fn ($query) => $type::paymentsQuery($query) + ->orderByRaw('nr, member_id'), + ]) + ->get() + ->filter(fn (Member $member) => $member->payments->count() > 0); + + return $this->toPages($members); + } + + /** + * @param class-string<Letter> $type + * + * @return EloquentCollection<Member> + */ + private function allMemberCollection(string $type, string $billKind): Collection + { + return Member::whereHas('billKind', fn (Builder $q) => $q->where('name', $billKind)) + ->with([ + 'payments' => fn ($query) => $type::paymentsQuery($query) + ->orderByRaw('nr, member_id'), + ]) + ->get() + ->filter(fn (Member $member) => $member->payments->count() > 0); + } + + /** + * @param class-string<Letter> $type + * @param Collection<int, Page> $pages + */ + private function resolve(string $type, Collection $pages): Letter + { + return new $type($pages); + } + + /** + * @param EloquentCollection<Member> $members + * + * @return Collection<int, Page> + */ + private function toPages(EloquentCollection $members): Collection + { + return $members->groupBy( + fn ($member) => Str::slug( + "{$member->lastname}{$member->address}{$member->zip}{$member->location}", + ), + )->map(fn ($page) => new Page($page)); + } +} diff --git a/app/Letter/Letter.php b/app/Letter/Letter.php new file mode 100644 index 00000000..88b6afa0 --- /dev/null +++ b/app/Letter/Letter.php @@ -0,0 +1,108 @@ +<?php + +namespace App\Letter; + +use App\Payment\Payment; +use Carbon\Carbon; +use Exception; +use Generator; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Collection; +use Illuminate\Support\Str; +use Zoomyboy\Tex\Document; +use Zoomyboy\Tex\Engine; +use Zoomyboy\Tex\Template; + +abstract class Letter extends Document +{ + abstract public function getSubject(): string; + + abstract public function view(): string; + + abstract public function linkLabel(): string; + + abstract public function sendAllLabel(): string; + + /** + * @param HasMany<Payment> $query + * + * @return HasMany<Payment> + */ + abstract public static function paymentsQuery(HasMany $query): HasMany; + + /** + * @return array<int, string> + */ + abstract public function getDescription(): array; + + abstract public function afterSingle(Payment $payment): void; + + /** + * @var Collection<int, Page> + */ + public Collection $pages; + public string $subject; + protected string $filename; + public string $until; + + /** + * @param Collection<int, Page> $pages + */ + public function __construct(Collection $pages) + { + $this->pages = $pages; + $this->subject = $this->getSubject(); + $this->until = now()->addWeeks(2)->format('d.m.Y'); + $this->setFilename(Str::slug("{$this->getSubject()} für {$pages->first()?->familyName}")); + } + + public function number(int $number): string + { + return number_format($number / 100, 2, '.', ''); + } + + public function getUntil(): Carbon + { + return now()->addWeeks(2); + } + + public function getEngine(): Engine + { + return Engine::XELATEX; + } + + public function basename(): string + { + return $this->filename; + } + + public function template(): Template + { + return Template::make('tex.templates.default'); + } + + public function setFilename(string $filename): self + { + $this->filename = $filename; + + return $this; + } + + public function getRecipient(): MailRecipient + { + if (!$this->pages->first()?->email) { + throw new Exception('Cannot get Recipient. Mail not set.'); + } + + return new MailRecipient($this->pages->first()->email, $this->pages->first()->familyName); + } + + public function allPayments(): Generator + { + foreach ($this->pages as $page) { + foreach ($page->getPayments() as $payment) { + yield $payment; + } + } + } +} diff --git a/app/Bill/BillSettings.php b/app/Letter/LetterSettings.php similarity index 91% rename from app/Bill/BillSettings.php rename to app/Letter/LetterSettings.php index 354e4aea..d8aee48d 100644 --- a/app/Bill/BillSettings.php +++ b/app/Letter/LetterSettings.php @@ -1,10 +1,10 @@ <?php -namespace App\Bill; +namespace App\Letter; use App\Setting\LocalSettings; -class BillSettings extends LocalSettings +class LetterSettings extends LocalSettings { public string $from_long; diff --git a/app/Letter/MailRecipient.php b/app/Letter/MailRecipient.php new file mode 100644 index 00000000..b59ae145 --- /dev/null +++ b/app/Letter/MailRecipient.php @@ -0,0 +1,15 @@ +<?php + +namespace App\Letter; + +class MailRecipient +{ + public string $name; + public string $email; + + public function __construct(string $email, string $name) + { + $this->email = $email; + $this->name = $name; + } +} diff --git a/app/Letter/Page.php b/app/Letter/Page.php new file mode 100644 index 00000000..520c91d1 --- /dev/null +++ b/app/Letter/Page.php @@ -0,0 +1,68 @@ +<?php + +namespace App\Letter; + +use App\Member\Member; +use App\Payment\Payment; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Collection as BaseCollection; + +class Page +{ + /** + * @var Collection<Member> + */ + private Collection $members; + public string $familyName; + public string $singleName; + public string $address; + public string $zip; + public string $location; + public string $usage; + public ?string $email; + /** + * @var array<string, string> + */ + public array $positions; + + /** + * @param Collection<Member> $members + */ + public function __construct(Collection $members) + { + $this->members = $members; + $this->familyName = $this->members->first()->lastname; + $this->singleName = $members->first()->lastname; + $this->address = $members->first()->address; + $this->zip = $members->first()->zip; + $this->location = $members->first()->location; + $this->email = $members->first()->email_parents ?: $members->first()->email; + $this->positions = $this->getPositions(); + $this->usage = "Mitgliedsbeitrag für {$this->familyName}"; + } + + /** + * @return array<string, string> + */ + public function getPositions(): array + { + return $this->getPayments()->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(); + } + + /** + * @return BaseCollection<int, Payment> + */ + public function getPayments(): BaseCollection + { + return $this->members->pluck('payments')->flatten(1); + } + + public function number(int $number): string + { + return number_format($number / 100, 2, '.', ''); + } +} diff --git a/app/Pdf/RememberType.php b/app/Letter/RememberDocument.php similarity index 77% rename from app/Pdf/RememberType.php rename to app/Letter/RememberDocument.php index 4b6c86a5..3d92cfc4 100644 --- a/app/Pdf/RememberType.php +++ b/app/Letter/RememberDocument.php @@ -1,20 +1,13 @@ <?php -namespace App\Pdf; +namespace App\Letter; -use App\Member\Member; use App\Payment\Payment; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Support\Collection; -class RememberType extends Repository implements LetterRepository +class RememberDocument extends Letter { - public string $filename; - - public function getPayments(Member $member): Collection - { - return $member->payments()->whereNeedsRemember()->get(); - } - public function linkLabel(): string { return 'Erinnerung erstellen'; @@ -37,21 +30,11 @@ class RememberType extends Repository implements LetterRepository return $this->filename; } - public function getScript(): EnvType - { - return EnvType::XELATEX; - } - - public function getView(): string + public function view(): string { return 'tex.remember'; } - public function getTemplate(): ?string - { - return 'default'; - } - public function getPositions(Collection $page): array { $memberIds = $page->pluck('id')->toArray(); @@ -65,11 +48,6 @@ class RememberType extends Repository implements LetterRepository })->toArray(); } - public function getFamilyName(Collection $page): string - { - return $page->first()->lastname; - } - public function getAddress(Collection $page): string { return $page->first()->address; @@ -92,10 +70,10 @@ class RememberType extends Repository implements LetterRepository public function getUsage(Collection $page): string { - return "Mitgliedsbeitrag für {$this->getFamilyName($page)}"; + return "Mitgliedsbeitrag für {$page->familyName}"; } - public function allLabel(): string + public function sendAllLabel(): string { return 'Erinnerungen versenden'; } @@ -122,4 +100,14 @@ class RememberType extends Repository implements LetterRepository { return 'Zahlungserinnerung'; } + + /** + * @param HasMany<Payment> $query + * + * @return HasMany<Payment> + */ + public static function paymentsQuery(HasMany $query): HasMany + { + return $query->whereNeedsRemember(); + } } diff --git a/app/Bill/SettingIndexAction.php b/app/Letter/SettingIndexAction.php similarity index 84% rename from app/Bill/SettingIndexAction.php rename to app/Letter/SettingIndexAction.php index 22667792..34c0cca1 100644 --- a/app/Bill/SettingIndexAction.php +++ b/app/Letter/SettingIndexAction.php @@ -1,6 +1,6 @@ <?php -namespace App\Bill; +namespace App\Letter; use Inertia\Inertia; use Inertia\Response; @@ -13,7 +13,7 @@ class SettingIndexAction /** * @return array<string, string> */ - public function handle(BillSettings $settings): array + public function handle(LetterSettings $settings): array { return [ 'from_long' => $settings->from_long, @@ -27,7 +27,7 @@ class SettingIndexAction ]; } - public function asController(BillSettings $settings): Response + public function asController(LetterSettings $settings): Response { session()->put('menu', 'setting'); session()->put('title', 'Rechnungs-Einstellungen'); diff --git a/app/Bill/SettingSaveAction.php b/app/Letter/SettingSaveAction.php similarity index 93% rename from app/Bill/SettingSaveAction.php rename to app/Letter/SettingSaveAction.php index 32a18104..aff1cbd6 100644 --- a/app/Bill/SettingSaveAction.php +++ b/app/Letter/SettingSaveAction.php @@ -1,6 +1,6 @@ <?php -namespace App\Bill; +namespace App\Letter; use Illuminate\Http\RedirectResponse; use Lorisleiva\Actions\ActionRequest; @@ -15,7 +15,7 @@ class SettingSaveAction */ public function handle(array $input): void { - $settings = app(BillSettings::class); + $settings = app(LetterSettings::class); $settings->fill([ 'from_long' => $input['from_long'] ?? '', diff --git a/app/Member/Member.php b/app/Member/Member.php index 420e96a5..88af06f3 100644 --- a/app/Member/Member.php +++ b/app/Member/Member.php @@ -3,11 +3,11 @@ namespace App\Member; use App\Activity; -use App\Bill\BillKind; use App\Confession; use App\Country; use App\Course\Models\CourseMember; use App\Group; +use App\Letter\BillKind; use App\Nationality; use App\Payment\Payment; use App\Payment\Subscription; diff --git a/app/Member/MemberController.php b/app/Member/MemberController.php index ce520185..676f8aef 100644 --- a/app/Member/MemberController.php +++ b/app/Member/MemberController.php @@ -3,12 +3,12 @@ namespace App\Member; use App\Activity; -use App\Bill\BillKind; use App\Confession; use App\Country; use App\Gender; use App\Http\Controllers\Controller; use App\Http\Views\MemberView; +use App\Letter\BillKind; use App\Nationality; use App\Payment\Subscription; use App\Region; diff --git a/app/Payment/ActionFactory.php b/app/Payment/ActionFactory.php index a9b2c4f2..85ecadb7 100644 --- a/app/Payment/ActionFactory.php +++ b/app/Payment/ActionFactory.php @@ -2,31 +2,19 @@ namespace App\Payment; -use App\Member\Member; -use App\Pdf\PdfRepository; -use App\Pdf\PdfRepositoryFactory; +use App\Letter\DocumentFactory; +use App\Letter\Letter; use Illuminate\Support\Collection; class ActionFactory { - public function forMember(Member $member): Collection - { - return app(PdfRepositoryFactory::class)->getTypes()->map(function (PdfRepository $repo) use ($member): array { - return [ - 'href' => route('member.singlepdf', ['member' => $member, 'type' => get_class($repo)]), - 'label' => $repo->linkLabel(), - 'disabled' => !$repo->createable($member), - ]; - }); - } - public function allLinks(): Collection { - return app(PdfRepositoryFactory::class)->getTypes()->map(function (PdfRepository $repo) { + return app(DocumentFactory::class)->getTypes()->map(function (Letter $repo) { return [ 'link' => [ 'href' => route('sendpayment.pdf', ['type' => get_class($repo)]), - 'label' => $repo->allLabel(), + 'label' => $repo->sendAllLabel(), ], 'text' => $repo->getDescription(), ]; diff --git a/app/Payment/PaymentController.php b/app/Payment/PaymentController.php index ded1ab5d..600f8866 100644 --- a/app/Payment/PaymentController.php +++ b/app/Payment/PaymentController.php @@ -3,25 +3,12 @@ namespace App\Payment; use App\Http\Controllers\Controller; -use App\Http\Views\MemberView; use App\Member\Member; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Inertia\Response; class PaymentController extends Controller { - public function index(Request $request, Member $member): Response - { - session()->put('menu', 'member'); - session()->put('title', "Zahlungen für Mitglied {$member->fullname}"); - - $payload = app(MemberView::class)->index($request, []); - $payload['single'] = app(MemberView::class)->paymentIndex($member); - - return \Inertia::render('member/VIndex', $payload); - } - public function store(Request $request, Member $member): RedirectResponse { $member->createPayment($request->validate([ @@ -33,17 +20,6 @@ class PaymentController extends Controller return redirect()->back(); } - public function edit(Member $member, Request $request, Payment $payment): Response - { - session()->put('menu', 'member'); - session()->put('title', "Zahlungen für Mitglied {$member->fullname}"); - - $payload = app(MemberView::class)->index($request, []); - $payload['single'] = app(MemberView::class)->paymentEdit($member, $payment); - - return \Inertia::render('member/VIndex', $payload); - } - public function update(Request $request, Member $member, Payment $payment): RedirectResponse { $payment->update($request->validate([ diff --git a/app/Payment/PaymentMail.php b/app/Payment/PaymentMail.php index 910442c3..45a6fefd 100644 --- a/app/Payment/PaymentMail.php +++ b/app/Payment/PaymentMail.php @@ -2,7 +2,7 @@ namespace App\Payment; -use App\Pdf\LetterRepository; +use App\Letter\Letter; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; @@ -13,18 +13,20 @@ class PaymentMail extends Mailable use Queueable; use SerializesModels; - public LetterRepository $repo; + public Letter $letter; public string $filename; + public string $salutation; /** * Create a new message instance. * * @return void */ - public function __construct(LetterRepository $repo, string $filename) + public function __construct(Letter $letter, string $filename) { + $this->letter = $letter; $this->filename = $filename; - $this->repo = $repo; + $this->salutation = 'Liebe Familie '.$letter->pages->first()->familyName; } /** @@ -34,11 +36,11 @@ class PaymentMail extends Mailable */ public function build() { - $template = Str::snake(class_basename($this->repo)); + $template = Str::snake(class_basename($this->letter)); return $this->markdown('mail.payment.'.$template) ->attach($this->filename) ->replyTo('kasse@stamm-silva.de') - ->subject($this->repo->getMailSubject().' | DPSG Stamm Silva'); + ->subject($this->letter->getSubject().' | DPSG Stamm Silva'); } } diff --git a/app/Payment/PaymentSendCommand.php b/app/Payment/PaymentSendCommand.php deleted file mode 100644 index 3fbc0e27..00000000 --- a/app/Payment/PaymentSendCommand.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -namespace App\Payment; - -use App\Pdf\BillType; -use App\Pdf\PdfGenerator; -use App\Pdf\PdfRepositoryFactory; -use App\Pdf\RememberType; -use Illuminate\Console\Command; -use Mail; - -class PaymentSendCommand extends Command -{ - /** - * The name and signature of the console command. - * - * @var string - */ - protected $signature = 'payment:send'; - - /** - * The console command description. - * - * @var string - */ - protected $description = 'Sends Bills'; - - /** - * Create a new command instance. - * - * @return void - */ - public function __construct() - { - parent::__construct(); - } - - /** - * Execute the console command. - * - * @return int - */ - public function handle() - { - $this->sendBills(); - $this->sendRemembers(); - - return 0; - } - - private function sendBills(): void - { - $repos = app(PdfRepositoryFactory::class)->repoCollection(BillType::class, 'E-Mail'); - - foreach ($repos as $repo) { - $generator = app(PdfGenerator::class)->setRepository($repo)->render(); - $to = (object) [ - 'email' => $repo->getEmail($repo->pages->first()), - 'name' => $repo->getFamilyName($repo->pages->first()), - ]; - Mail::to($to)->send(new PaymentMail($repo, $generator->getCompiledFilename())); - app(PdfRepositoryFactory::class)->afterSingle($repo); - } - } - - private function sendRemembers(): void - { - $repos = app(PdfRepositoryFactory::class)->repoCollection(RememberType::class, 'E-Mail'); - - foreach ($repos as $repo) { - $generator = app(PdfGenerator::class)->setRepository($repo)->render(); - $to = (object) [ - 'email' => $repo->getEmail($repo->pages->first()), - 'name' => $repo->getFamilyName($repo->pages->first()), - ]; - Mail::to($to)->send(new PaymentMail($repo, $generator->getCompiledFilename())); - app(PdfRepositoryFactory::class)->afterSingle($repo); - } - } -} diff --git a/app/Payment/SendpaymentController.php b/app/Payment/SendpaymentController.php index 1ef21ffd..a5628fb3 100644 --- a/app/Payment/SendpaymentController.php +++ b/app/Payment/SendpaymentController.php @@ -3,13 +3,13 @@ namespace App\Payment; use App\Http\Controllers\Controller; -use App\Pdf\PdfGenerator; -use App\Pdf\PdfRepositoryFactory; +use App\Letter\DocumentFactory; use Illuminate\Contracts\Support\Responsable; use Illuminate\Http\Request; use Illuminate\Http\Response; use Inertia\Inertia; use Inertia\Response as InertiaResponse; +use Zoomyboy\Tex\Tex; class SendpaymentController extends Controller { @@ -28,13 +28,15 @@ class SendpaymentController extends Controller */ public function send(Request $request) { - $repo = app(PdfRepositoryFactory::class)->forAll($request->type, 'Post'); + $repo = app(DocumentFactory::class)->forAll($request->type, 'Post'); - $pdfFile = app(PdfGenerator::class)->setRepository($repo)->render(); - app(PdfRepositoryFactory::class)->afterAll($request->type, 'Post'); + if (is_null($repo)) { + return response()->noContent(); + } - return null === $repo - ? response()->noContent() - : $pdfFile; + $pdfFile = Tex::compile($repo); + app(DocumentFactory::class)->afterAll($request->type, 'Post'); + + return $pdfFile; } } diff --git a/app/Pdf/BillType.php b/app/Pdf/BillType.php deleted file mode 100644 index 60a897ca..00000000 --- a/app/Pdf/BillType.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php - -namespace App\Pdf; - -use App\Member\Member; -use App\Payment\Payment; -use Illuminate\Support\Collection; - -class BillType extends Repository implements LetterRepository -{ - public string $filename; - - public function getPayments(Member $member): Collection - { - return $member->payments()->whereNeedsBill()->get(); - } - - public function linkLabel(): string - { - return 'Rechnung erstellen'; - } - - public function getSubject(): string - { - return 'Rechnung'; - } - - public function setFilename(string $filename): static - { - $this->filename = $filename; - - return $this; - } - - public function getScript(): EnvType - { - return EnvType::XELATEX; - } - - public function getFilename(): string - { - return $this->filename; - } - - public function getView(): string - { - return 'tex.bill'; - } - - public function getTemplate(): ?string - { - return 'default'; - } - - public function getPositions(Collection $page): array - { - $memberIds = $page->pluck('id')->toArray(); - $payments = Payment::whereIn('member_id', $memberIds) - ->orderByRaw('nr, member_id')->whereNeedsBill()->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 getFamilyName(Collection $page): string - { - return $page->first()->lastname; - } - - 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 getUsage(Collection $page): string - { - return "Mitgliedsbeitrag für {$this->getFamilyName($page)}"; - } - - public function allLabel(): string - { - return 'Rechnungen versenden'; - } - - /** - * Get Descriptions for sendpayment page. - * - * @return array<int, string> - */ - public function getDescription(): array - { - return [ - 'Diese Funktion erstellt ein PDF mit allen noch nicht versendenden Rechnungen bei den Mitgliedern die Post als Versandweg haben.', - 'Die Rechnungen werden automatisch auf "Rechnung gestellt" aktualisiert.', - ]; - } - - public function afterSingle(Payment $payment): void - { - $payment->update(['status_id' => 2]); - } - - public function getMailSubject(): string - { - return 'Jahresrechnung'; - } -} diff --git a/app/Pdf/Data/MemberEfzData.php b/app/Pdf/Data/MemberEfzData.php deleted file mode 100644 index 725c0ac4..00000000 --- a/app/Pdf/Data/MemberEfzData.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -namespace App\Pdf\Data; - -use App\Member\Member; -use App\Pdf\EnvType; -use App\Pdf\PdfRepository; -use Illuminate\Http\Request; -use Illuminate\Support\Str; -use Spatie\LaravelData\Data; - -class MemberEfzData extends Data implements PdfRepository -{ - public function __construct( - public ?string $name, - public ?string $secondLine, - public ?string $currentDate, - public ?array $sender = [], - public ?string $filename = '', - ) { - } - - public static function fromRequest(Request $request): self - { - $memberId = $request->member; - - $member = Member::findOrFail($memberId); - - return new self( - name: $member->fullname, - secondLine: "geb. am {$member->birthday->format('d.m.Y')}, wohnhaft in {$member->location}", - currentDate: now()->format('d.m.Y'), - sender: [ - $member->fullname, - $member->address, - $member->zip.' '.$member->location, - 'Mglnr.: '.$member->nami_id, - ] - ); - } - - public function getFilename(): string - { - return 'efz-fuer-'.Str::slug($this->name); - } - - public function getView(): string - { - return 'tex.efz'; - } - - public function getTemplate(): ?string - { - return 'efz'; - } - - public function setFilename(string $filename): static - { - $this->filename = $filename; - - return $this; - } - - public function getScript(): EnvType - { - return EnvType::PDFLATEX; - } -} diff --git a/app/Pdf/EnvType.php b/app/Pdf/EnvType.php deleted file mode 100644 index 2afb1ee3..00000000 --- a/app/Pdf/EnvType.php +++ /dev/null @@ -1,9 +0,0 @@ -<?php - -namespace App\Pdf; - -enum EnvType: string -{ - case XELATEX = 'XELATEX'; - case PDFLATEX = 'PDFLATEX'; -} diff --git a/app/Pdf/LetterRepository.php b/app/Pdf/LetterRepository.php deleted file mode 100644 index af545d64..00000000 --- a/app/Pdf/LetterRepository.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -namespace App\Pdf; - -use App\Member\Member; -use App\Payment\Payment; -use Carbon\Carbon; -use Generator; -use Illuminate\Support\Collection; - -interface LetterRepository extends PdfRepository -{ - public function getSubject(): string; - - public function getPositions(Collection $page): array; - - public function getFamilyName(Collection $page): string; - - public function getAddress(Collection $page): string; - - public function getZip(Collection $page): string; - - public function getLocation(Collection $page): string; - - public function createable(Member $member): bool; - - public function getPayments(Member $member): Collection; - - public function linkLabel(): string; - - public function getUntil(): Carbon; - - public function getUsage(Collection $page): string; - - public function allLabel(): string; - - public function getEmail(Collection $page): string; - - public function getDescription(): array; - - public function afterSingle(Payment $payment): void; - - public function getMailSubject(): string; - - public function allPayments(): Generator; -} diff --git a/app/Pdf/MemberPdfController.php b/app/Pdf/MemberPdfController.php index 7dae0cd9..9cf9a255 100644 --- a/app/Pdf/MemberPdfController.php +++ b/app/Pdf/MemberPdfController.php @@ -3,10 +3,12 @@ namespace App\Pdf; use App\Http\Controllers\Controller; +use App\Letter\DocumentFactory; use App\Member\Member; use Illuminate\Contracts\Support\Responsable; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Zoomyboy\Tex\Tex; class MemberPdfController extends Controller { @@ -15,10 +17,10 @@ class MemberPdfController extends Controller */ public function __invoke(Request $request, Member $member) { - $repo = app(PdfRepositoryFactory::class)->fromSingleRequest($request->type, $member); + $document = app(DocumentFactory::class)->fromSingleRequest($request->type, $member); - return null === $repo + return null === $document ? response()->noContent() - : app(PdfGenerator::class)->setRepository($repo)->render(); + : Tex::compile($document); } } diff --git a/app/Pdf/PdfGenerator.php b/app/Pdf/PdfGenerator.php deleted file mode 100644 index fc0d5164..00000000 --- a/app/Pdf/PdfGenerator.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -namespace App\Pdf; - -use Illuminate\Contracts\Support\Responsable; -use Illuminate\Support\Str; -use Storage; - -class PdfGenerator implements Responsable -{ - private ?string $filename = null; - private PdfRepository $repo; - private string $dir; - - public function setRepository(PdfRepository $repo): self - { - $this->repo = $repo; - - return $this; - } - - public function render(): self - { - $this->filename = $this->repo->getFilename(); - $this->dir = Str::random(32); - - Storage::disk('temp')->put($this->dir.'/'.$this->repo->getFilename().'.tex', $this->compileView()); - Storage::disk('temp')->makeDirectory($this->dir); - - if ($this->repo->getTemplate()) { - $this->copyTemplateTo(Storage::disk('temp')->path($this->dir)); - } - - $command = 'cd '.Storage::disk('temp')->path($this->dir); - $command .= ' && '.env($this->repo->getScript()->value).' --halt-on-error '.$this->repo->getFilename().'.tex'; - $command .= ' && '.env($this->repo->getScript()->value).' --halt-on-error '.$this->repo->getFilename().'.tex'; - exec($command, $output, $returnVar); - - return $this; - } - - public function compileView(): string - { - return view()->make($this->repo->getView(), [ - 'data' => $this->repo, - ])->render(); - } - - public function toResponse($request) - { - return response()->file($this->getCompiledFilename(), [ - 'Content-Type' => 'application/pdf', - 'Content-Disposition' => "inline; filename=\"{$this->filename}.pdf\"", - ]); - } - - public function getCompiledFilename(): string - { - return Storage::disk('temp')->path($this->dir.'/'.$this->filename.'.pdf'); - } - - private function copyTemplateTo(string $destination): void - { - $templatePath = resource_path("views/tex/templates/{$this->repo->getTemplate()}"); - exec('cp '.$templatePath.'/* '.$destination); - } -} diff --git a/app/Pdf/PdfRepository.php b/app/Pdf/PdfRepository.php deleted file mode 100644 index f392ebcb..00000000 --- a/app/Pdf/PdfRepository.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -namespace App\Pdf; - -interface PdfRepository -{ - public function setFilename(string $filename): static; - - public function getFilename(): string; - - public function getView(): string; - - public function getTemplate(): ?string; - - public function getScript(): EnvType; -} diff --git a/app/Pdf/PdfRepositoryFactory.php b/app/Pdf/PdfRepositoryFactory.php deleted file mode 100644 index 0067700c..00000000 --- a/app/Pdf/PdfRepositoryFactory.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -namespace App\Pdf; - -use App\Member\Member; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Support\Collection; -use Illuminate\Support\Str; - -class PdfRepositoryFactory -{ - /** - * @var array<int, class-string<PdfRepository>> - */ - private array $types = [ - BillType::class, - RememberType::class, - ]; - - /** - * @return Collection<int, PdfRepository> - */ - public function getTypes(): Collection - { - return collect(array_map(fn ($classString) => new $classString(collect()), $this->types)); - } - - public function fromSingleRequest(string $type, Member $member): ?LetterRepository - { - $members = $this->singleMemberCollection($member, $type); - - if ($members->isEmpty()) { - return null; - } - - $repo = $this->resolve($type, $members); - $firstMember = $members->first()->first(); - - return $repo->setFilename( - Str::slug("{$repo->getSubject()} für {$firstMember->lastname}"), - ); - } - - public function forAll(string $type, string $billKind): ?PdfRepository - { - $members = $this->toMemberGroup($this->allMemberCollection($type, $billKind)); - - if ($members->isEmpty()) { - return null; - } - - return $this->resolve($type, $members)->setFilename('alle-rechnungen'); - } - - public function repoCollection(string $type, string $billKind): Collection - { - $members = $this->toMemberGroup($this->allMemberCollection($type, $billKind)); - - return $members->map(function (Collection $members) use ($type) { - $repo = $this->resolve($type, collect([$members])); - - return $repo->setFilename(Str::slug("{$repo->getSubject()} für {$members->first()->lastname}")); - }); - } - - public function afterSingle(LetterRepository $repo): void - { - foreach ($repo->allPayments() as $payment) { - $repo->afterSingle($payment); - } - } - - public function afterAll(string $type, string $billKind): void - { - $members = $this->allMemberCollection($type, $billKind); - $repo = $this->resolve($type, $this->toMemberGroup($members)); - - $this->afterSingle($repo); - } - - public function singleMemberCollection(Member $member, string $type): Collection - { - $members = Member::where($member->only(['lastname', 'address', 'zip', 'location'])) - ->get() - ->filter(fn (Member $member) => app($type)->createable($member)); - - return $this->toMemberGroup($members); - } - - private function allMemberCollection(string $type, string $billKind): Collection - { - return Member::whereHas('billKind', fn (Builder $q) => $q->where('name', $billKind)) - ->get() - ->filter(fn (Member $member) => app($type)->createable($member)); - } - - private function resolve(string $kind, Collection $members): LetterRepository - { - return new $kind($members); - } - - private function toMemberGroup(Collection $members): Collection - { - return $members->groupBy( - fn ($member) => Str::slug( - "{$member->lastname}{$member->address}{$member->zip}{$member->location}", - ), - ); - } -} diff --git a/app/Pdf/Repository.php b/app/Pdf/Repository.php deleted file mode 100644 index 4b603783..00000000 --- a/app/Pdf/Repository.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -namespace App\Pdf; - -use App\Member\Member; -use Carbon\Carbon; -use Generator; -use Illuminate\Support\Collection; - -abstract class Repository -{ - abstract public function getPayments(Member $member): Collection; - - public Collection $pages; - - public function __construct(Collection $pages) - { - $this->pages = $pages; - } - - public function number(int $number): string - { - return number_format($number / 100, 2, '.', ''); - } - - public function getUntil(): Carbon - { - return now()->addWeeks(2); - } - - public function createable(Member $member): bool - { - return 0 !== $this->getPayments($member)->count(); - } - - public function allPayments(): Generator - { - foreach ($this->pages as $page) { - foreach ($page as $member) { - foreach ($this->getPayments($member) as $payment) { - yield $payment; - } - } - } - } -} diff --git a/app/Setting/SettingServiceProvider.php b/app/Setting/SettingServiceProvider.php index eda8489b..a8e59cf7 100644 --- a/app/Setting/SettingServiceProvider.php +++ b/app/Setting/SettingServiceProvider.php @@ -2,7 +2,7 @@ namespace App\Setting; -use App\Bill\BillSettings; +use App\Letter\LetterSettings; use App\Mailman\MailmanSettings; use Illuminate\Support\ServiceProvider; @@ -25,7 +25,7 @@ class SettingServiceProvider extends ServiceProvider */ public function boot() { - app(SettingFactory::class)->register(BillSettings::class); + app(SettingFactory::class)->register(LetterSettings::class); app(SettingFactory::class)->register(MailmanSettings::class); } } diff --git a/database/factories/Bill/BillKindFactory.php b/database/factories/Letter/BillKindFactory.php similarity index 87% rename from database/factories/Bill/BillKindFactory.php rename to database/factories/Letter/BillKindFactory.php index 1adfbe1b..853bc478 100644 --- a/database/factories/Bill/BillKindFactory.php +++ b/database/factories/Letter/BillKindFactory.php @@ -1,8 +1,8 @@ <?php -namespace Database\Factories\Bill; +namespace Database\Factories\Letter; -use App\Bill\BillKind; +use App\Letter\BillKind; use Illuminate\Database\Eloquent\Factories\Factory; /** diff --git a/database/factories/Member/MemberFactory.php b/database/factories/Member/MemberFactory.php index 40892479..b3c685e2 100644 --- a/database/factories/Member/MemberFactory.php +++ b/database/factories/Member/MemberFactory.php @@ -5,6 +5,7 @@ namespace Database\Factories\Member; use App\Country; use App\Fee; use App\Group; +use App\Letter\BillKind; use App\Member\Member; use App\Nationality; use App\Payment\Payment; @@ -60,6 +61,20 @@ class MemberFactory extends Factory ->for($subscription); } + public function postBillKind(): self + { + return $this->state([ + 'bill_kind_id' => BillKind::firstWhere('name', 'Post')->id, + ]); + } + + public function emailBillKind(): self + { + return $this->state([ + 'bill_kind_id' => BillKind::firstWhere('name', 'E-Mail')->id, + ]); + } + public function inNami(int $namiId): self { return $this->state(['nami_id' => $namiId]); diff --git a/database/migrations/2020_04_12_223230_create_members_table.php b/database/migrations/2020_04_12_223230_create_members_table.php index 3cedc6c0..448717f4 100644 --- a/database/migrations/2020_04_12_223230_create_members_table.php +++ b/database/migrations/2020_04_12_223230_create_members_table.php @@ -1,6 +1,6 @@ <?php -use App\Bill\BillKind; +use App\Letter\BillKind; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; diff --git a/packages/tex b/packages/tex index fb9d7a66..2ffab167 160000 --- a/packages/tex +++ b/packages/tex @@ -1 +1 @@ -Subproject commit fb9d7a660c6e170eedf7a11b02f7cfeff14346ab +Subproject commit 2ffab167ea1628c3b24724c3506b90e5f7120103 diff --git a/resources/views/mail/payment/bill_type.blade.php b/resources/views/mail/payment/bill_document.blade.php similarity index 80% rename from resources/views/mail/payment/bill_type.blade.php rename to resources/views/mail/payment/bill_document.blade.php index 14b90e51..70d441c7 100644 --- a/resources/views/mail/payment/bill_type.blade.php +++ b/resources/views/mail/payment/bill_document.blade.php @@ -1,5 +1,5 @@ @component('mail::message') -# Liebe Familie {{ $repo->getFamilyName($repo->pages->first()) }}, +# {{ $salutation }}, Im Anhang findet ihr die aktuelle Rechnung des Stammes Silva für das laufende Jahr. Bitte begleicht diese bis zum angegebenen Datum. diff --git a/resources/views/mail/payment/remember_type.blade.php b/resources/views/mail/payment/remember_document.blade.php similarity index 82% rename from resources/views/mail/payment/remember_type.blade.php rename to resources/views/mail/payment/remember_document.blade.php index fd14a79a..ff703955 100644 --- a/resources/views/mail/payment/remember_type.blade.php +++ b/resources/views/mail/payment/remember_document.blade.php @@ -1,5 +1,5 @@ @component('mail::message') -# Liebe Familie {{ $repo->getFamilyName($repo->pages->first()) }}, +# {{ $salutation }}, Hiermit möchten wir euch an die noch ausstehenden Mitgliedsbeiträge des Stammes Silva für das laufende Jahr erinnern. Bitte begleicht diese bis zum angegebenen Datum. diff --git a/resources/views/tex/bill.tex b/resources/views/tex/bill.tex index 0fe1c01a..78a4e7c2 100644 --- a/resources/views/tex/bill.tex +++ b/resources/views/tex/bill.tex @@ -1,19 +1,19 @@ \documentclass[silvaletter,12pt]{scrlttr2} -\setkomavar{subject}{<<< $data->getSubject() >>>} +\setkomavar{subject}{<<< $subject >>>} \begin{document} -@foreach($data->pages as $page) -\begin{letter}{Familie <<< $data->getFamilyName($page) >>>\\<<< $data->getAddress($page) >>>\\<<< $data->getZip($page) >>> <<< $data->getLocation($page) >>>} +@foreach($pages as $page) +\begin{letter}{Familie <<< $page->familyName >>>\\<<< $page->address >>>\\<<< $page->zip >>> <<< $page->location >>>} \sffamily \gdef\TotalHT{0} - \opening{Liebe Familie <<< $data->getFamilyName($page) >>>,} + \opening{Liebe Familie <<< $page->familyName >>>,} Hiermit stellen wir Ihnen den aktuellen Mitgliedsbeitrag für den \usekomavar*{fromname} und die DPSG in Rechnung. Dieser setzt sich wie folgt zusammen: \begin{center} \begin{tabular}{@{}p{0.5\textwidth}|r} - @foreach($data->getPositions($page) as $desc => $price) + @foreach($page->positions as $desc => $price) \product{<<< $desc >>>}{<<< $price >>>} @endforeach \hline @@ -21,13 +21,13 @@ \end{tabular} \end{center} - Somit bitten wir Sie, den ausstehenden Betrag von \totalttc bis zum \textbf{<<< $data->getUntil()->format('d.m.Y') >>>} auf folgendes Konto zu überweisen: + Somit bitten wir Sie, den ausstehenden Betrag von \totalttc bis zum \textbf{<<< $until >>>} auf folgendes Konto zu überweisen: \begin{tabular}{ll} Kontoinhaber: & DPSG Stamm Silva \\ IBAN: & DE40 3425 0000 0000 2145 51 \\ Bic: & SOLSDE33XXX \\ - Verwendungszweck: & <<<$data->getUsage($page)>>> + Verwendungszweck: & <<<$page->usage>>> \end{tabular} Bitte nehmen Sie zur Kenntnis, dass der für jedes Mitglied obligatorische Versicherungsschutz über die DPSG nur dann für Ihr Kind / Ihre Kinder gilt, wenn der Mitgliedsbeitrag bezahlt wurde. Wenn dies nicht geschieht, müssen wir Ihr Kind / Ihre Kinder von allen Pfadfinderaktionen ausschließen. Dazu gehören sowohl die Gruppenstunden sowie Tagesaktionen als auch mehrtägige Lager. diff --git a/resources/views/tex/remember.tex b/resources/views/tex/remember.tex index 4b116d78..1710d9dd 100644 --- a/resources/views/tex/remember.tex +++ b/resources/views/tex/remember.tex @@ -1,19 +1,19 @@ \documentclass[silvaletter,12pt]{scrlttr2} -\setkomavar{subject}{<<< $data->getSubject() >>>} +\setkomavar{subject}{<<< $subject >>>} \begin{document} -@foreach($data->pages as $page) -\begin{letter}{Familie <<< $data->getFamilyName($page) >>>\\<<< $data->getAddress($page) >>>\\<<< $data->getZip($page) >>> <<< $data->getLocation($page) >>>} +@foreach($pages as $page) +\begin{letter}{Familie <<< $page->familyName >>>\\<<< $page->address >>>\\<<< $page->zip >>> <<< $page->location >>>} \sffamily \gdef\TotalHT{0} - \opening{Liebe Familie <<< $data->getFamilyName($page) >>>,} + \opening{Liebe Familie <<< $page->familyName >>>,} Ihr Mitgliedbeitrag ist noch ausstehend. Dieser setzt sich wie folgt zusammen: \begin{center} \begin{tabular}{@{}p{0.5\textwidth}|r} - @foreach($data->getPositions($page) as $desc => $price) + @foreach($page->positions as $desc => $price) \product{<<< $desc >>>}{<<< $price >>>} @endforeach \hline @@ -21,13 +21,13 @@ \end{tabular} \end{center} - Somit bitten wir Sie, den ausstehenden Betrag von \totalttc bis zum \textbf{<<< $data->getUntil()->format('d.m.Y') >>>} auf folgendes Konto zu überweisen: + Somit bitten wir Sie, den ausstehenden Betrag von \totalttc bis zum \textbf{<<< $until >>>} auf folgendes Konto zu überweisen: \begin{tabular}{ll} Kontoinhaber: & DPSG Stamm Silva \\ IBAN: & DE40 3425 0000 0000 2145 51 \\ Bic: & SOLSDE33XXX \\ - Verwendungszweck: & <<<$data->getUsage($page)>>> + Verwendungszweck: & <<<$page->usage>>> \end{tabular} Bitte nehmen Sie zur Kenntnis, dass der für jedes Mitglied obligatorische Versicherungsschutz über die DPSG nur dann für Ihr Kind / Ihre Kinder gilt, wenn der Mitgliedsbeitrag bezahlt wurde. Wenn dies nicht geschieht, müssen wir Ihr Kind / Ihre Kinder von allen Pfadfinderaktionen ausschließen. Dazu gehören sowohl die Gruppenstunden sowie Tagesaktionen als auch mehrtägige Lager. diff --git a/resources/views/tex/templates/zuschussdv/teilnahmeliste.pdf b/resources/views/tex/templates/contribution/teilnahmeliste.pdf similarity index 100% rename from resources/views/tex/templates/zuschussdv/teilnahmeliste.pdf rename to resources/views/tex/templates/contribution/teilnahmeliste.pdf diff --git a/resources/views/tex/zuschuss-dv.tex b/resources/views/tex/zuschuss-dv.tex index 6ed4184f..7e2661ea 100644 --- a/resources/views/tex/zuschuss-dv.tex +++ b/resources/views/tex/zuschuss-dv.tex @@ -13,21 +13,21 @@ \begin{document} \noindent \sffamily -@foreach($data->members()->chunk(17) as $chunk) +@foreach($members as $chunk) \begin{tikzpicture}[remember picture,overlay,yscale=-1] - \node[anchor=base west] at (38mm,41.62mm) {\bfseries{\large{<<<!!$data->dateRange()!!>>>}}}; -\node[anchor=base west] at (135.2mm,41.62mm) {\bfseries{\large{<<<!!$data->zipLocation!!>>>}}}; - \node[anchor=base west] at (242.7mm,41.62mm) {\bfseries{\large{<<<!!$data->countryName()!!>>>}}}; + \node[anchor=base west] at (38mm,41.62mm) {\bfseries{\large{<<<!!$dateRange!!>>>}}}; +\node[anchor=base west] at (135.2mm,41.62mm) {\bfseries{\large{<<<!!$zipLocation!!>>>}}}; + \node[anchor=base west] at (242.7mm,41.62mm) {\bfseries{\large{<<<!!$countryName!!>>>}}}; \node[thick, cross out,draw=black,text width=2.4mm, text height=2.4mm, inner sep=0mm] at (17.76mm,47.10mm) {}; @foreach($chunk as $i => $member) \node[anchor=base, text width=7.75mm, align=center] at ($(16.35mm, 76.6mm + 7mm * <<<$i % 17>>>)$) {<<<$i+1>>>}; - \node[anchor=base, text width=18mm, align=center] at ($(32.55mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$data->memberShort($member)>>>}; - \node[anchor=base, text width=70mm, align=center] at ($(80.25mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$data->memberName($member)>>>}; - \node[anchor=base, text width=118mm, align=center] at ($(178.25mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$data->memberAddress($member)>>>}; - \node[anchor=base, text width=16mm, align=center] at ($(249.50mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$data->memberGender($member)>>>}; - \node[anchor=base, text width=16mm, align=center] at ($(269.50mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$data->memberAge($member)>>>}; + \node[anchor=base, text width=18mm, align=center] at ($(32.55mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$memberShort($member)>>>}; + \node[anchor=base, text width=70mm, align=center] at ($(80.25mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$memberName($member)>>>}; + \node[anchor=base, text width=118mm, align=center] at ($(178.25mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$memberAddress($member)>>>}; + \node[anchor=base, text width=16mm, align=center] at ($(249.50mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$memberGender($member)>>>}; + \node[anchor=base, text width=16mm, align=center] at ($(269.50mm, 76.6mm + 7mm * <<<$i%17>>>)$) {<<<$memberAge($member)>>>}; @endforeach \end{tikzpicture} diff --git a/resources/views/tex/zuschuss-stadt.tex b/resources/views/tex/zuschuss-stadt.tex index 4ba9c0b5..7b22a98d 100644 --- a/resources/views/tex/zuschuss-stadt.tex +++ b/resources/views/tex/zuschuss-stadt.tex @@ -83,11 +83,11 @@ \newcommand{\emptycheckbox}{\tikz{\node[text height=0.5cm,text width=0.5cm,inner sep=0cm] at (0,0) {};}} \begin{document} \sffamily -@foreach($data->members()->chunk(14) as $chunk) +@foreach($memberModels as $chunk) \begin{tikzpicture}[outer] \path (current page.north west) ++(1cm,-1cm) coordinate (OL) -- (current page.north east) ++(-1cm,0cm) coordinate (OR) node[midway,below=0.5cm] {\textbf{TEILNEHMER - / INNENLISTE}}; \matrix (options) at ($(OL)+(0.5cm,-1cm)$) [matrix of nodes, column sep=0cm,row sep=0.5cm,nodes in empty cells, every node/.style={inner sep=0cm,align=left,text width=6.2cm}, anchor=north west] { - <<<!!$data->checkboxes()!!>>> + <<<!!$checkboxes!!>>> }; \node[align=left,inner sep=0cm,anchor=west] at (options-2-4.west) {\tikz{\node[draw,very thick,rectangle,text height=0.5cm,text width=0.5cm,inner sep=0cm] (checkbox) at (0,0) {}; \draw[thick] (checkbox.south east) ++(0.2cm,0) -- (checkbox.south east -| options-2-4.south east);}}; @@ -95,13 +95,13 @@ \draw (org.south east -| options-2-2.south west) -- (org.south east -| options-2-4.south east) node[formfill] {DPSG Stamm Silva Solingen Wald}; \node[anchor=north west] (title) at ($(org.south west)+(0cm,-0.5cm)$) {\large{Titel der Maßnahme:}}; - \draw (title.south east -| options-2-2.south west) -- (title.south east -| options-2-4.south east) node[formfill] {<<<$data->eventName>>>}; + \draw (title.south east -| options-2-2.south west) -- (title.south east -| options-2-4.south east) node[formfill] {<<<$eventName>>>}; \node[anchor=north west] (datefrom) at ($(title.south west)+(0cm,-0.5cm)$) {\large{Datum vom:}}; - \draw (datefrom.south east -| options-2-2.south west) -- ($(datefrom.south east -| options-2-2.south east) - (1,0cm)$) node[formfill] {<<<$data->niceEventFrom()>>>}; + \draw (datefrom.south east -| options-2-2.south west) -- ($(datefrom.south east -| options-2-2.south east) - (1,0cm)$) node[formfill] {<<<$niceEventFrom()>>>}; \node[anchor=south west] (dateuntil) at (options-2-3.south west |- datefrom.south west) {\large{bis:}}; - \draw[label={east:aaa}] (dateuntil.south east) -- (datefrom.south east -| options-2-3.south east) node[formfill] {<<<$data->niceEventTo()>>>}; + \draw[label={east:aaa}] (dateuntil.south east) -- (datefrom.south east -| options-2-3.south east) node[formfill] {<<<$niceEventUntil()>>>}; \path[fill=yellow] (datefrom.south -| OL) ++(0,-1.0) rectangle ($(datefrom.south -| OR) + (0,-1.5)$); diff --git a/routes/web.php b/routes/web.php index 178b8c4e..16416424 100644 --- a/routes/web.php +++ b/routes/web.php @@ -25,7 +25,7 @@ Route::group(['middleware' => 'auth:web'], function (): void { Route::get('/initialize', InitializeFormAction::class)->name('initialize.form'); Route::post('/initialize', InitializeAction::class)->name('initialize.store'); Route::resource('member', MemberController::class); - Route::resource('member.payment', PaymentController::class); + Route::apiResource('member.payment', PaymentController::class); Route::resource('allpayment', AllpaymentController::class); Route::resource('subscription', SubscriptionController::class); Route::post('/member/{member}/confirm', MemberConfirmController::class); diff --git a/tests/Feature/Bill/SettingTest.php b/tests/Feature/Bill/SettingTest.php index 27d9ecf7..0f4ae2fa 100644 --- a/tests/Feature/Bill/SettingTest.php +++ b/tests/Feature/Bill/SettingTest.php @@ -2,7 +2,7 @@ namespace Tests\Feature\Bill; -use App\Bill\BillSettings; +use App\Letter\LetterSettings; use Illuminate\Foundation\Testing\DatabaseTransactions; use Tests\TestCase; @@ -13,7 +13,7 @@ class SettingTest extends TestCase public function testSettingIndex(): void { $this->withoutExceptionHandling()->login()->loginNami(); - BillSettings::fake([ + LetterSettings::fake([ 'from_long' => 'DPSG Stamm Muster', 'from' => 'Stamm Muster', 'mobile' => '+49 176 55555', @@ -74,7 +74,7 @@ class SettingTest extends TestCase ]); $response->assertRedirect('/setting/bill'); - $settings = app(BillSettings::class); + $settings = app(LetterSettings::class); $this->assertEquals('DPSG Stamm Muster', $settings->from_long); } } diff --git a/tests/Feature/ContributionTest.php b/tests/Feature/ContributionTest.php new file mode 100644 index 00000000..428ccabb --- /dev/null +++ b/tests/Feature/ContributionTest.php @@ -0,0 +1,44 @@ +<?php + +namespace Tests\Feature; + +use App\Country; +use App\Member\Member; +use Illuminate\Foundation\Testing\DatabaseTransactions; +use Tests\TestCase; +use Zoomyboy\Tex\Tex; + +class ContributionTest extends TestCase +{ + use DatabaseTransactions; + + /** + * @testWith ["App\\Contribution\\SolingenDocument", ["Super tolles Lager", "Max Muster", "Jane Muster", "15.06.1991"]] + * ["App\\Contribution\\DvDocument", ["Muster, Max", "Muster, Jane", "15.06.1991", "42777 SG"]] + * + * @param array<int, string> $bodyChecks + */ + public function testItCompilesContributionDocuments(string $type, array $bodyChecks): void + { + $this->withoutExceptionHandling(); + Tex::spy(); + $this->login()->loginNami(); + $country = Country::factory()->create(); + $member1 = Member::factory()->defaults()->create(['firstname' => 'Max', 'lastname' => 'Muster']); + $member2 = Member::factory()->defaults()->create(['firstname' => 'Jane', 'lastname' => 'Muster']); + + $response = $this->call('GET', '/contribution/generate', [ + 'country' => $country->id, + 'dateFrom' => '1991-06-15', + 'dateUntil' => '1991-06-16', + 'eventName' => 'Super tolles Lager', + 'members' => [$member1->id, $member2->id], + 'type' => $type, + 'zipLocation' => '42777 SG', + ]); + + $response->assertSessionDoesntHaveErrors(); + $response->assertOk(); + Tex::assertCompiled($type, fn ($document) => $document->hasAllContent($bodyChecks)); + } +} diff --git a/tests/Feature/Letter/LetterSendActionTest.php b/tests/Feature/Letter/LetterSendActionTest.php new file mode 100644 index 00000000..1a0f67c1 --- /dev/null +++ b/tests/Feature/Letter/LetterSendActionTest.php @@ -0,0 +1,56 @@ +<?php + +namespace Tests\Feature\Letter; + +use App\Letter\Actions\LetterSendAction; +use App\Letter\BillDocument; +use App\Member\Member; +use App\Payment\Payment; +use App\Payment\PaymentMail; +use Illuminate\Foundation\Testing\DatabaseTransactions; +use Illuminate\Support\Facades\Storage; +use Mail; +use Tests\TestCase; +use Zoomyboy\Tex\Tex; + +class LetterSendActionTest extends TestCase +{ + use DatabaseTransactions; + + public Member $member; + + public function setUp(): void + { + parent::setUp(); + + Storage::fake('local'); + $this->withoutExceptionHandling(); + $this->login()->loginNami(); + $this->member = Member::factory() + ->defaults() + ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) + ->emailBillKind() + ->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']); + } + + public function testItCanCreatePdfPayments(): void + { + Mail::fake(); + + LetterSendAction::run(); + + Mail::assertSent(PaymentMail::class, fn ($mail) => Storage::path('rechnung-fur-mom.pdf') === $mail->filename); + } + + public function testItCanCompileAttachment(): void + { + Mail::fake(); + Tex::spy(); + + LetterSendAction::run(); + + Tex::assertCompiled(BillDocument::class, fn ($document) => 'Mom' === $document->pages->first()->familyName + && $document->pages->first()->getPositions() === ['Beitrag 1997 für Lah Mom (tollerbeitrag)' => '54.00'] + ); + } +} diff --git a/tests/Feature/Member/StoreTest.php b/tests/Feature/Member/StoreTest.php index ee425ede..80984f69 100644 --- a/tests/Feature/Member/StoreTest.php +++ b/tests/Feature/Member/StoreTest.php @@ -3,10 +3,10 @@ namespace Tests\Feature\Member; use App\Activity; -use App\Bill\BillKind; use App\Country; use App\Fee; use App\Gender; +use App\Letter\BillKind; use App\Member\CreateJob; use App\Member\Member; use App\Nationality; diff --git a/tests/Feature/Pdf/GenerateTest.php b/tests/Feature/Pdf/GenerateTest.php index cf3394e8..163b7a22 100644 --- a/tests/Feature/Pdf/GenerateTest.php +++ b/tests/Feature/Pdf/GenerateTest.php @@ -5,12 +5,11 @@ namespace Tests\Feature\Pdf; use App\Country; use App\Fee; use App\Group; +use App\Letter\BillDocument; +use App\Letter\DocumentFactory; use App\Member\Member; use App\Nationality; use App\Payment\Subscription; -use App\Pdf\BillType; -use App\Pdf\PdfGenerator; -use App\Pdf\PdfRepositoryFactory; use Carbon\Carbon; use Database\Factories\Member\MemberFactory; use Database\Factories\Payment\PaymentFactory; @@ -38,18 +37,18 @@ class GenerateTest extends TestCase 'no_pdf_when_no_bill' => [ 'members' => [ [ - 'factory' => fn (MemberFactory $member): MemberFactory => $member, + 'factory' => fn (MemberFactory $member) => $member, 'payments' => [], ], ], 'urlCallable' => fn (Collection $members): int => $members->first()->id, - 'type' => BillType::class, + 'type' => BillDocument::class, 'filename' => null, ], 'bill_for_single_member_when_no_bill_received_yet' => [ 'members' => [ [ - 'factory' => fn (MemberFactory $member): MemberFactory => $member + 'factory' => fn (MemberFactory $member) => $member ->state([ 'firstname' => '::firstname::', 'lastname' => '::lastname::', @@ -58,7 +57,7 @@ class GenerateTest extends TestCase 'location' => '::location::', ]), 'payments' => [ - fn (PaymentFactory $payment): PaymentFactory => $payment + fn (PaymentFactory $payment) => $payment ->notPaid() ->nr('1995') ->subscription('::subName::', 1500), @@ -66,7 +65,7 @@ class GenerateTest extends TestCase ], ], 'urlCallable' => fn (Collection $members): int => $members->first()->id, - 'type' => BillType::class, + 'type' => BillDocument::class, 'filename' => 'rechnung-fur-lastname.pdf', 'output' => [ 'Rechnung', @@ -79,19 +78,19 @@ class GenerateTest extends TestCase 'bill_has_deadline' => [ 'members' => [ [ - 'factory' => fn (MemberFactory $member): MemberFactory => $member + 'factory' => fn (MemberFactory $member) => $member ->state([ 'firstname' => '::firstname::', 'lastname' => '::lastname::', ]), 'payments' => [ - fn (PaymentFactory $payment): PaymentFactory => $payment + fn (PaymentFactory $payment) => $payment ->nr('A')->notPaid()->subscription('::subName::', 1500), ], ], ], 'urlCallable' => fn (Collection $members): int => $members->first()->id, - 'type' => BillType::class, + 'type' => BillDocument::class, 'filename' => 'rechnung-fur-lastname.pdf', 'output' => [ '29.04.2021', @@ -100,7 +99,7 @@ class GenerateTest extends TestCase 'families' => [ 'members' => [ [ - 'factory' => fn (MemberFactory $member): MemberFactory => $member + 'factory' => fn (MemberFactory $member) => $member ->state([ 'firstname' => '::firstname1::', 'lastname' => '::lastname::', @@ -109,12 +108,12 @@ class GenerateTest extends TestCase 'location' => '::location::', ]), 'payments' => [ - fn (PaymentFactory $payment): PaymentFactory => $payment + fn (PaymentFactory $payment) => $payment ->nr('::nr::')->notPaid()->subscription('::subName::', 1500), ], ], [ - 'factory' => fn (MemberFactory $member): MemberFactory => $member + 'factory' => fn (MemberFactory $member) => $member ->state([ 'firstname' => '::firstname2::', 'lastname' => '::lastname::', @@ -123,13 +122,13 @@ class GenerateTest extends TestCase 'location' => '::location::', ]), 'payments' => [ - fn (PaymentFactory $payment): PaymentFactory => $payment + fn (PaymentFactory $payment) => $payment ->nr('::nr2::')->notPaid()->subscription('::subName2::', 1600), ], ], ], 'urlCallable' => fn (Collection $members): int => $members->first()->id, - 'type' => BillType::class, + 'type' => BillDocument::class, 'filename' => 'rechnung-fur-lastname.pdf', 'output' => [ '::nr::', @@ -155,7 +154,7 @@ class GenerateTest extends TestCase $urlId = call_user_func($urlCallable, $members); $member = Member::find($urlId); - $repo = app(PdfRepositoryFactory::class)->fromSingleRequest($type, $member); + $repo = app(DocumentFactory::class)->fromSingleRequest($type, $member); if (null === $filename) { $this->assertNull($repo); @@ -163,7 +162,7 @@ class GenerateTest extends TestCase return; } - $content = app(PdfGenerator::class)->setRepository($repo)->compileView(); + $content = $repo->renderBody(); foreach ($output as $out) { $this->assertStringContainsString($out, $content); diff --git a/tests/Feature/Sendpayment/SendpaymentTest.php b/tests/Feature/Sendpayment/SendpaymentTest.php new file mode 100644 index 00000000..72586d92 --- /dev/null +++ b/tests/Feature/Sendpayment/SendpaymentTest.php @@ -0,0 +1,68 @@ +<?php + +namespace Tests\Feature\Sendpayment; + +use App\Letter\BillDocument; +use App\Member\Member; +use App\Payment\Payment; +use App\Payment\Status; +use Illuminate\Foundation\Testing\DatabaseTransactions; +use Tests\TestCase; +use Zoomyboy\Tex\Tex; + +class SendpaymentTest extends TestCase +{ + use DatabaseTransactions; + + public function testItCanViewSendpaymentPage(): void + { + $this->withoutExceptionHandling(); + $this->login()->loginNami(); + + $response = $this->get(route('sendpayment.create')); + + $response->assertOk(); + $this->assertInertiaHas('Rechnungen versenden', $response, 'types.0.link.label'); + $href = $this->inertia($response, 'types.0.link.href'); + $this->assertStringContainsString('BillDocument', $href); + } + + public function testItCanCreatePdfPayments(): void + { + Tex::spy(); + $this->withoutExceptionHandling(); + $this->login()->loginNami(); + $member = Member::factory() + ->defaults() + ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) + ->has(Payment::factory()->paid()->nr('1998')->subscription('bezahltdesc', 5800)) + ->postBillKind() + ->create(); + + $response = $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Letter\\BillDocument']); + + $response->assertOk(); + $this->assertEquals(Status::firstWhere('name', 'Rechnung gestellt')->id, $member->payments->firstWhere('nr', '1997')->status_id); + $this->assertEquals(Status::firstWhere('name', 'Rechnung beglichen')->id, $member->payments->firstWhere('nr', '1998')->status_id); + Tex::assertCompiled(BillDocument::class, fn ($document) => $document->hasAllContent(['1997', 'tollerbeitrag', '54.00']) + && $document->missesAllContent(['1998', 'bezahltdesc', '58.00']) + ); + } + + public function testItDoesntCreatePdfsWhenUserHasEmail(): void + { + Tex::spy(); + $this->withoutExceptionHandling(); + $this->login()->loginNami(); + $member = Member::factory() + ->defaults() + ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', 5400)) + ->emailBillKind() + ->create(); + + $response = $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Letter\\BillDocument']); + + $response->assertStatus(204); + Tex::assertNotCompiled(BillDocument::class); + } +}