Add payment_data for payments

This commit is contained in:
Philipp Lang 2023-11-30 23:54:16 +01:00
parent fc1b647b54
commit 566ed704a6
24 changed files with 880 additions and 963 deletions

View File

@ -5,6 +5,7 @@ namespace App\Invoice\Actions;
use App\Invoice\BillKind; use App\Invoice\BillKind;
use App\Invoice\DocumentFactory; use App\Invoice\DocumentFactory;
use App\Invoice\Queries\BillKindQuery; use App\Invoice\Queries\BillKindQuery;
use App\Payment\Payment;
use App\Payment\PaymentMail; use App\Payment\PaymentMail;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
@ -33,13 +34,13 @@ class InvoiceSendAction
public function handle(): int public function handle(): int
{ {
foreach (app(DocumentFactory::class)->getTypes() as $type) { foreach (app(DocumentFactory::class)->getTypes() as $type) {
$invoices = app(DocumentFactory::class)->invoiceCollection($type, new BillKindQuery(BillKind::EMAIL)); $memberCollection = (new BillKindQuery(BillKind::EMAIL))->type($type)->getMembers();
foreach ($invoices as $invoice) { foreach ($memberCollection as $members) {
$invoice = $type::fromMembers($members);
$invoicePath = Storage::disk('temp')->path(Tex::compile($invoice)->storeIn('', 'temp')); $invoicePath = Storage::disk('temp')->path(Tex::compile($invoice)->storeIn('', 'temp'));
Mail::to($invoice->getRecipient()) Mail::to($invoice->getRecipient())->send(new PaymentMail($invoice, $invoicePath));
->send(new PaymentMail($invoice, $invoicePath)); app(DocumentFactory::class)->afterSingle($invoice, $members);
app(DocumentFactory::class)->afterSingle($invoice);
} }
} }

View File

@ -29,7 +29,10 @@ class BillDocument extends Invoice
public function afterSingle(Payment $payment): void public function afterSingle(Payment $payment): void
{ {
$payment->update(['status_id' => 2]); $payment->update([
'invoice_data' => $this->toArray(),
'status_id' => 2,
]);
} }
public function getMailSubject(): string public function getMailSubject(): string

View File

@ -2,7 +2,7 @@
namespace App\Invoice; namespace App\Invoice;
use App\Invoice\Queries\InvoiceMemberQuery; use App\Member\Member;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
class DocumentFactory class DocumentFactory
@ -24,44 +24,14 @@ class DocumentFactory
} }
/** /**
* @param class-string<Invoice> $type * @param Collection<(int|string), Member> $members
*/ */
public function singleInvoice(string $type, InvoiceMemberQuery $query): ?Invoice public function afterSingle(Invoice $invoice, Collection $members): void
{ {
$pages = $query->getPages($type); foreach ($members as $member) {
foreach ($member->payments as $payment) {
if ($pages->isEmpty()) { $invoice->afterSingle($payment);
return null; }
} }
return $this->resolve($type, $pages);
}
/**
* @param class-string<Invoice> $type
*
* @return Collection<int, Invoice>
*/
public function invoiceCollection(string $type, InvoiceMemberQuery $query): Collection
{
return $query
->getPages($type)
->map(fn ($page) => $this->resolve($type, collect([$page])));
}
public function afterSingle(Invoice $invoice): void
{
foreach ($invoice->allPayments() as $payment) {
$invoice->afterSingle($payment);
}
}
/**
* @param class-string<Invoice> $type
* @param Collection<int, Page> $pages
*/
private function resolve(string $type, Collection $pages): Invoice
{
return new $type($pages);
} }
} }

View File

@ -2,10 +2,9 @@
namespace App\Invoice; namespace App\Invoice;
use App\Member\Member;
use App\Payment\Payment; use App\Payment\Payment;
use Carbon\Carbon;
use Exception; use Exception;
use Generator;
use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Str; use Illuminate\Support\Str;
@ -16,11 +15,9 @@ use Zoomyboy\Tex\Template;
abstract class Invoice extends Document abstract class Invoice extends Document
{ {
abstract public function getSubject(): string; abstract public function getSubject(): string;
abstract public function view(): string; abstract public function view(): string;
abstract public function afterSingle(Payment $payment): void;
abstract public function linkLabel(): string; abstract public function linkLabel(): string;
abstract public static function sendAllLabel(): string; abstract public static function sendAllLabel(): string;
/** /**
@ -35,37 +32,46 @@ abstract class Invoice extends Document
*/ */
abstract public static function getDescription(): array; abstract public static 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; public string $until;
public InvoiceSettings $settings; public string $filename;
/** /**
* @param Collection<int, Page> $pages * @param array<string, string> $positions
*/ */
public function __construct(Collection $pages) public function __construct(
{ public string $familyName,
$this->pages = $pages; public string $singleName,
$this->subject = $this->getSubject(); public string $address,
public string $zip,
public string $location,
public array $positions,
public string $usage,
public ?string $email,
) {
$this->until = now()->addWeeks(2)->format('d.m.Y'); $this->until = now()->addWeeks(2)->format('d.m.Y');
$this->setFilename(Str::slug("{$this->getSubject()} für {$pages->first()?->familyName}")); $this->filename = Str::slug("{$this->getSubject()} für {$familyName}");
$this->settings = app(InvoiceSettings::class);
} }
public function number(int $number): string /**
* @param Collection<(int|string), Member> $members
*/
public static function fromMembers(Collection $members): self
{ {
return number_format($number / 100, 2, '.', ''); return static::withoutMagicalCreationFrom([
'familyName' => $members->first()->lastname,
'singleName' => $members->first()->lastname,
'address' => $members->first()->address,
'zip' => $members->first()->zip,
'location' => $members->first()->location,
'email' => $members->first()->email_parents ?: $members->first()->email,
'positions' => static::renderPositions($members),
'usage' => "Mitgliedsbeitrag für {$members->first()->lastname}",
]);
} }
public function getUntil(): Carbon public function settings(): InvoiceSettings
{ {
return now()->addWeeks(2); return app(InvoiceSettings::class);
} }
public function getEngine(): Engine public function getEngine(): Engine
@ -92,20 +98,9 @@ abstract class Invoice extends Document
public function getRecipient(): MailRecipient public function getRecipient(): MailRecipient
{ {
if (!$this->pages->first()?->email) { throw_unless($this->email, Exception::class, 'Cannot get Recipient. Mail not set.');
throw new Exception('Cannot get Recipient. Mail not set.');
}
return new MailRecipient($this->pages->first()->email, $this->pages->first()->familyName); return new MailRecipient($this->email, $this->familyName);
}
public function allPayments(): Generator
{
foreach ($this->pages as $page) {
foreach ($page->getPayments() as $payment) {
yield $payment;
}
}
} }
/** /**
@ -113,12 +108,38 @@ abstract class Invoice extends Document
*/ */
public function mailView(): string public function mailView(): string
{ {
$view = 'mail.payment.'.Str::snake(class_basename($this)); $view = 'mail.payment.' . Str::snake(class_basename($this));
if (!view()->exists($view)) { throw_unless(view()->exists($view), Exception::class, 'Mail view ' . $view . ' existiert nicht.');
throw new Exception('Mail view '.$view.' existiert nicht.');
}
return $view; return $view;
} }
/**
* @param Collection<int|string, Member> $members
*
* @return array<string, string>
*/
public static function renderPositions(Collection $members): array
{
/** @var array<string, string> */
$result = [];
foreach ($members->pluck('payments')->flatten(1) as $payment) {
if ($payment->subscription->split) {
foreach ($payment->subscription->children as $child) {
$result["{$payment->subscription->name} ({$child->name}) {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname}"] = static::number($child->amount);
}
} else {
$result["{$payment->subscription->name} {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname}"] = static::number($payment->subscription->getAmount());
}
}
return $result;
}
public static function number(int $number): string
{
return number_format($number / 100, 2, '.', '');
}
} }

View File

@ -1,76 +0,0 @@
<?php
namespace App\Invoice;
use App\Member\Member;
use App\Payment\Payment;
use Illuminate\Support\Collection;
class Page
{
/**
* @var Collection<int, 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<int, 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
{
/** @var array<string, string> */
$result = [];
foreach ($this->getPayments() as $payment) {
if ($payment->subscription->split) {
foreach ($payment->subscription->children as $child) {
$result["{$payment->subscription->name} ({$child->name}) {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname}"] = $this->number($child->amount);
}
} else {
$result["{$payment->subscription->name} {$payment->nr} für {$payment->member->firstname} {$payment->member->lastname}"] = $this->number($payment->subscription->getAmount());
}
}
return $result;
}
/**
* @return Collection<int, Payment>
*/
public function getPayments(): Collection
{
return $this->members->pluck('payments')->flatten(1);
}
public function number(int $number): string
{
return number_format($number / 100, 2, '.', '');
}
}

View File

@ -3,7 +3,6 @@
namespace App\Invoice\Queries; namespace App\Invoice\Queries;
use App\Invoice\Invoice; use App\Invoice\Invoice;
use App\Invoice\Page;
use App\Member\Member; use App\Member\Member;
use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection as EloquentCollection; use Illuminate\Database\Eloquent\Collection as EloquentCollection;
@ -12,35 +11,46 @@ use Illuminate\Support\Str;
abstract class InvoiceMemberQuery abstract class InvoiceMemberQuery
{ {
/**
* @param class-string<Invoice> $type
*/
public string $type;
/** /**
* @return Builder<Member> * @return Builder<Member>
*/ */
abstract protected function getQuery(): Builder; abstract protected function getQuery(): Builder;
/** /**
* @param class-string<Invoice> $type * @return Collection<(int|string), EloquentCollection<(int|string), Member>>
*
* @return Collection<int, Page>
*/ */
public function getPages(string $type): Collection public function getMembers(): Collection
{ {
return $this->get($type)->groupBy( return $this->get()->groupBy(
fn ($member) => Str::slug( fn ($member) => Str::slug(
"{$member->lastname}{$member->address}{$member->zip}{$member->location}", "{$member->lastname}{$member->address}{$member->zip}{$member->location}",
), ),
)->map(fn ($page) => new Page($page)); )->toBase();
} }
/** /**
* @param class-string<Invoice> $type * @param class-string<Invoice> $type
* */
public function type(string $type): self
{
$this->type = $type;
return $this;
}
/**
* @return EloquentCollection<int, Member> * @return EloquentCollection<int, Member>
*/ */
private function get(string $type): EloquentCollection private function get(): EloquentCollection
{ {
return $this->getQuery() return $this->getQuery()
->with([ ->with([
'payments' => fn ($query) => $type::paymentsQuery($query) 'payments' => fn ($query) => $this->type::paymentsQuery($query)
->orderByRaw('nr, member_id'), ->orderByRaw('nr, member_id'),
]) ])
->get() ->get()

View File

@ -1,22 +0,0 @@
<?php
namespace App\Invoice\Queries;
use App\Member\Member;
use Illuminate\Database\Eloquent\Builder;
class SingleMemberQuery extends InvoiceMemberQuery
{
public function __construct(
private Member $member
) {
}
/**
* @return Builder<Member>
*/
protected function getQuery(): Builder
{
return Member::where($this->member->only(['lastname', 'address', 'zip', 'location']));
}
}

View File

@ -12,7 +12,18 @@ class Payment extends Model
{ {
use HasFactory; use HasFactory;
public $fillable = ['member_id', 'subscription_id', 'nr', 'status_id', 'last_remembered_at']; /**
* @var array<int, string>
*/
public $fillable = ['member_id', 'invoice_data', 'subscription_id', 'nr', 'status_id', 'last_remembered_at'];
/**
* @var array<string, string>
*/
public $casts = [
'invoice_data' => 'json',
'last_remembered_at' => 'date',
];
/** /**
* @return BelongsTo<Member, self> * @return BelongsTo<Member, self>

View File

@ -25,7 +25,7 @@ class PaymentMail extends Mailable
{ {
$this->invoice = $invoice; $this->invoice = $invoice;
$this->filename = $filename; $this->filename = $filename;
$this->salutation = 'Liebe Familie ' . $invoice->pages->first()->familyName; $this->salutation = 'Liebe Familie ' . $invoice->familyName;
} }
/** /**

View File

@ -3,6 +3,7 @@
namespace App\Payment; namespace App\Payment;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Invoice\BillDocument;
use App\Invoice\BillKind; use App\Invoice\BillKind;
use App\Invoice\DocumentFactory; use App\Invoice\DocumentFactory;
use App\Invoice\Queries\BillKindQuery; use App\Invoice\Queries\BillKindQuery;
@ -30,15 +31,19 @@ class SendpaymentController extends Controller
*/ */
public function send(Request $request) public function send(Request $request)
{ {
$invoice = app(DocumentFactory::class)->singleInvoice($request->type, new BillKindQuery(BillKind::POST)); $memberCollection = (new BillKindQuery(BillKind::POST))->type($request->type)->getMembers();
if (is_null($invoice)) { if ($memberCollection->isEmpty()) {
return response()->noContent(); return response()->noContent();
} }
$pdfFile = Tex::compile($invoice); $documents = $memberCollection->map(function ($members) use ($request) {
app(DocumentFactory::class)->afterSingle($invoice); $document = $request->type::fromMembers($members);
app(DocumentFactory::class)->afterSingle($document, $members);
return $document;
});
return $pdfFile;
return Tex::merge($documents->all());
} }
} }

View File

@ -1,27 +0,0 @@
<?php
namespace App\Pdf;
use App\Http\Controllers\Controller;
use App\Invoice\DocumentFactory;
use App\Invoice\Queries\SingleMemberQuery;
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
{
/**
* @return Response|Responsable
*/
public function __invoke(Request $request, Member $member)
{
$invoice = app(DocumentFactory::class)->singleInvoice($request->type, new SingleMemberQuery($member));
return null === $invoice
? response()->noContent()
: Tex::compile($invoice);
}
}

1146
composer.lock generated

File diff suppressed because it is too large Load Diff

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 Illuminate\Support\Carbon;
use Tests\RequestFactories\Child; use Tests\RequestFactories\Child;
/** /**
@ -22,7 +23,7 @@ class PaymentFactory extends Factory
'nr' => $this->faker->year, 'nr' => $this->faker->year,
'subscription_id' => Subscription::factory()->create()->id, 'subscription_id' => Subscription::factory()->create()->id,
'status_id' => Status::factory()->create()->id, 'status_id' => Status::factory()->create()->id,
'last_remembered_at' => $this->faker->dateTime, 'last_remembered_at' => now(),
]; ];
} }
@ -31,6 +32,11 @@ class PaymentFactory extends Factory
return $this->for(Status::whereName('Nicht bezahlt')->first()); return $this->for(Status::whereName('Nicht bezahlt')->first());
} }
public function pending(): self
{
return $this->for(Status::whereName('Rechnung gestellt')->first())->state(['last_remembered_at' => now()->subYears(2)]);;
}
public function paid(): self public function paid(): self
{ {
return $this->for(Status::whereName('Rechnung beglichen')->first()); return $this->for(Status::whereName('Rechnung beglichen')->first());

View File

@ -0,0 +1,32 @@
<?php
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()
{
Schema::table('payments', function (Blueprint $table) {
$table->json('invoice_data')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('payments', function (Blueprint $table) {
$table->dropColumn('invoice_data');
});
}
};

@ -1 +1 @@
Subproject commit c5ea29af1bb1591238bb037da93739f5bd874334 Subproject commit af626f5d3a14a365e97dc6437025a0b1da6b42bc

@ -1 +1 @@
Subproject commit 6f162102ef7ceca41822d18c3e694abd926f550b Subproject commit bbab104f7e00c059ffffa115f6b769e2333e137a

View File

@ -304,91 +304,6 @@ parameters:
count: 1 count: 1
path: packages/laravel-nami/src/Gender.php path: packages/laravel-nami/src/Gender.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:__construct\\(\\) has parameter \\$options with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:__construct\\(\\) has parameter \\$response with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:__construct\\(\\) has parameter \\$title with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:__construct\\(\\) has parameter \\$url with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:fromHttp\\(\\) has no return type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:http\\(\\) has no return type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:http\\(\\) has parameter \\$options with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:http\\(\\) has parameter \\$response with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:http\\(\\) has parameter \\$title with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:http\\(\\) has parameter \\$url with no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Method Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:level\\(\\) has no return type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Property Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:\\$errors has no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Property Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:\\$options has no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Property Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:\\$response has no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Property Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:\\$title has no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Property Zoomyboy\\\\LaravelNami\\\\Logger\\:\\:\\$url has no type specified\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
-
message: "#^Unsafe usage of new static\\(\\)\\.$#"
count: 1
path: packages/laravel-nami/src/Logger.php
- -
message: "#^Method Zoomyboy\\\\LaravelNami\\\\LoginException\\:\\:setReason\\(\\) has no return type specified\\.$#" message: "#^Method Zoomyboy\\\\LaravelNami\\\\LoginException\\:\\:setReason\\(\\) has no return type specified\\.$#"
count: 1 count: 1

View File

@ -1,6 +1,6 @@
\documentclass[silvaletter,12pt]{scrlttr2} \documentclass[silvaletter,12pt]{scrlttr2}
\setkomavar{subject}{<<< $subject >>>} \setkomavar{subject}{<<< $getSubject >>>}
\setkomavar{fromname}[<<<$settings->from>>>]{<<<$settings->from_long>>>} \setkomavar{fromname}[<<<$settings->from>>>]{<<<$settings->from_long>>>}
\setkomavar{frommobilephone}[Mobiltelefon: ]{<<<$settings->mobile>>>} \setkomavar{frommobilephone}[Mobiltelefon: ]{<<<$settings->mobile>>>}
\setkomavar{fromemail}[E-Mail: ]{<<<$settings->email>>>} \setkomavar{fromemail}[E-Mail: ]{<<<$settings->email>>>}
@ -11,17 +11,16 @@
\setkomavar{fromlogo}{\includegraphics[width=2cm]{logo.png}} % stammeslogo \setkomavar{fromlogo}{\includegraphics[width=2cm]{logo.png}} % stammeslogo
\begin{document} \begin{document}
@foreach($pages as $page) \begin{letter}{Familie <<< $familyName >>>\\<<< $address >>>\\<<< $zip >>> <<< $location >>>}
\begin{letter}{Familie <<< $page->familyName >>>\\<<< $page->address >>>\\<<< $page->zip >>> <<< $page->location >>>}
\sffamily \sffamily
\gdef\TotalHT{0} \gdef\TotalHT{0}
\opening{Liebe Familie <<< $page->familyName >>>,} \opening{Liebe Familie <<< $familyName >>>,}
Hiermit stellen wir Ihnen den aktuellen Mitgliedsbeitrag für den \usekomavar*{fromname} und die DPSG in Rechnung. Dieser setzt sich wie folgt zusammen: 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{center}
\begin{tabular}{@{}p{0.8\textwidth}|r} \begin{tabular}{@{}p{0.8\textwidth}|r}
@foreach($page->positions as $desc => $price) @foreach($positions as $desc => $price)
\product{<<< $desc >>>}{<<< $price >>>} \product{<<< $desc >>>}{<<< $price >>>}
@endforeach @endforeach
\hline \hline
@ -35,7 +34,7 @@
Kontoinhaber: & <<<$settings->from_long>>> \\ Kontoinhaber: & <<<$settings->from_long>>> \\
IBAN: & <<<$settings->iban>>> \\ IBAN: & <<<$settings->iban>>> \\
Bic: & <<<$settings->bic>>> \\ Bic: & <<<$settings->bic>>> \\
Verwendungszweck: & <<<$page->usage>>> Verwendungszweck: & <<<$usage>>>
\end{tabular} \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. 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.
@ -44,6 +43,5 @@
\closing{Viele Grüße \\ Der Stammesvorstand} \closing{Viele Grüße \\ Der Stammesvorstand}
\end{letter} \end{letter}
@endforeach
\end{document} \end{document}

View File

@ -1,6 +1,6 @@
\documentclass[silvaletter,12pt]{scrlttr2} \documentclass[silvaletter,12pt]{scrlttr2}
\setkomavar{subject}{<<< $subject >>>} \setkomavar{subject}{<<< $getSubject >>>}
\setkomavar{fromname}[<<<$settings->from>>>]{<<<$settings->from_long>>>} \setkomavar{fromname}[<<<$settings->from>>>]{<<<$settings->from_long>>>}
\setkomavar{frommobilephone}[Mobiltelefon: ]{<<<$settings->mobile>>>} \setkomavar{frommobilephone}[Mobiltelefon: ]{<<<$settings->mobile>>>}
\setkomavar{fromemail}[E-Mail: ]{<<<$settings->email>>>} \setkomavar{fromemail}[E-Mail: ]{<<<$settings->email>>>}
@ -11,17 +11,16 @@
\setkomavar{fromlogo}{\includegraphics[width=2cm]{logo.png}} % stammeslogo \setkomavar{fromlogo}{\includegraphics[width=2cm]{logo.png}} % stammeslogo
\begin{document} \begin{document}
@foreach($pages as $page) \begin{letter}{Familie <<< $familyName >>>\\<<< $address >>>\\<<< $zip >>> <<< $location >>>}
\begin{letter}{Familie <<< $page->familyName >>>\\<<< $page->address >>>\\<<< $page->zip >>> <<< $page->location >>>}
\sffamily \sffamily
\gdef\TotalHT{0} \gdef\TotalHT{0}
\opening{Liebe Familie <<< $page->familyName >>>,} \opening{Liebe Familie <<< $familyName >>>,}
Ihr Mitgliedbeitrag ist noch ausstehend. Dieser setzt sich wie folgt zusammen: Ihr Mitgliedbeitrag ist noch ausstehend. Dieser setzt sich wie folgt zusammen:
\begin{center} \begin{center}
\begin{tabular}{@{}p{0.8\textwidth}|r} \begin{tabular}{@{}p{0.8\textwidth}|r}
@foreach($page->positions as $desc => $price) @foreach($positions as $desc => $price)
\product{<<< $desc >>>}{<<< $price >>>} \product{<<< $desc >>>}{<<< $price >>>}
@endforeach @endforeach
\hline \hline
@ -35,7 +34,7 @@
Kontoinhaber: & <<<$settings->from_long>>> \\ Kontoinhaber: & <<<$settings->from_long>>> \\
IBAN: & <<<$settings->iban>>> \\ IBAN: & <<<$settings->iban>>> \\
Bic: & <<<$settings->bic>>> \\ Bic: & <<<$settings->bic>>> \\
Verwendungszweck: & <<<$page->usage>>> Verwendungszweck: & <<<$usage>>>
\end{tabular} \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. 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.
@ -44,6 +43,5 @@
\closing{Viele Grüße \\ Der Stammesvorstand} \closing{Viele Grüße \\ Der Stammesvorstand}
\end{letter} \end{letter}
@endforeach
\end{document} \end{document}

View File

@ -52,7 +52,6 @@ use App\Payment\Actions\PaymentStoreAction;
use App\Payment\Actions\PaymentUpdateAction; use App\Payment\Actions\PaymentUpdateAction;
use App\Payment\SendpaymentController; use App\Payment\SendpaymentController;
use App\Payment\SubscriptionController; use App\Payment\SubscriptionController;
use App\Pdf\MemberPdfController;
Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void { Route::group(['namespace' => 'App\\Http\\Controllers'], function (): void {
Auth::routes(['register' => false]); Auth::routes(['register' => false]);
@ -71,8 +70,6 @@ Route::group(['middleware' => 'auth:web'], function (): void {
Route::get('allpayment', AllpaymentPageAction::class)->name('allpayment.page'); Route::get('allpayment', AllpaymentPageAction::class)->name('allpayment.page');
Route::post('allpayment', AllpaymentStoreAction::class)->name('allpayment.store'); Route::post('allpayment', AllpaymentStoreAction::class)->name('allpayment.store');
Route::resource('subscription', SubscriptionController::class); Route::resource('subscription', SubscriptionController::class);
Route::get('/member/{member}/pdf', MemberPdfController::class)
->name('member.singlepdf');
Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create'); Route::get('/sendpayment', [SendpaymentController::class, 'create'])->name('sendpayment.create');
Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf'); Route::get('/sendpayment/pdf', [SendpaymentController::class, 'send'])->name('sendpayment.pdf');
Route::get('/member/{member}/efz', ShowEfzDocumentAction::class)->name('efz'); Route::get('/member/{member}/efz', ShowEfzDocumentAction::class)->name('efz');

View File

@ -3,8 +3,11 @@
namespace Tests\Feature\Invoice; namespace Tests\Feature\Invoice;
use App\Invoice\BillDocument; use App\Invoice\BillDocument;
use App\Invoice\BillKind;
use App\Invoice\DocumentFactory; use App\Invoice\DocumentFactory;
use App\Invoice\Invoice;
use App\Invoice\InvoiceSettings; use App\Invoice\InvoiceSettings;
use App\Invoice\Queries\BillKindQuery;
use App\Invoice\Queries\InvoiceMemberQuery; use App\Invoice\Queries\InvoiceMemberQuery;
use App\Invoice\Queries\SingleMemberQuery; use App\Invoice\Queries\SingleMemberQuery;
use App\Invoice\RememberDocument; use App\Invoice\RememberDocument;
@ -15,7 +18,7 @@ use Tests\RequestFactories\Child;
use Tests\TestCase; use Tests\TestCase;
use Zoomyboy\Tex\Tex; use Zoomyboy\Tex\Tex;
class DocumentFactoryTest extends TestCase class BillRememberDocumentTest extends TestCase
{ {
use DatabaseTransactions; use DatabaseTransactions;
@ -30,13 +33,14 @@ class DocumentFactoryTest extends TestCase
'zip' => '::zip::', 'zip' => '::zip::',
'location' => '::location::', 'location' => '::location::',
]) ])
->postBillKind()
->has(Payment::factory()->notPaid()->nr('1995')->subscription('::subName::', [ ->has(Payment::factory()->notPaid()->nr('1995')->subscription('::subName::', [
new Child('a', 1000), new Child('a', 1000),
new Child('a', 500), new Child('a', 500),
])) ]))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice(BillDocument::class, $this->query($member)); $invoice = BillDocument::fromMembers($this->query(BillDocument::class)->getMembers()->first());
$invoice->assertHasAllContent([ $invoice->assertHasAllContent([
'Rechnung', 'Rechnung',
@ -51,6 +55,7 @@ class DocumentFactoryTest extends TestCase
{ {
$member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->postBillKind()
->state([ ->state([
'firstname' => '::firstname::', 'firstname' => '::firstname::',
'lastname' => '::lastname::', 'lastname' => '::lastname::',
@ -61,7 +66,7 @@ class DocumentFactoryTest extends TestCase
], ['split' => true])) ], ['split' => true]))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice(BillDocument::class, $this->query($member)); $invoice = BillDocument::fromMembers($this->query(BillDocument::class)->getMembers()->first());
$invoice->assertHasAllContent([ $invoice->assertHasAllContent([
'Rechnung', 'Rechnung',
@ -75,46 +80,48 @@ class DocumentFactoryTest extends TestCase
public function testBillSetsFilename(): void public function testBillSetsFilename(): void
{ {
$member = Member::factory() Member::factory()
->defaults() ->defaults()
->postBillKind()
->state(['lastname' => '::lastname::']) ->state(['lastname' => '::lastname::'])
->has(Payment::factory()->notPaid()->nr('1995')) ->has(Payment::factory()->notPaid()->nr('1995'))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice(BillDocument::class, $this->query($member)); $invoice = BillDocument::fromMembers($this->query(BillDocument::class)->getMembers()->first());
$this->assertEquals('rechnung-fur-lastname.pdf', $invoice->compiledFilename()); $this->assertEquals('rechnung-fur-lastname.pdf', $invoice->compiledFilename());
} }
public function testRememberSetsFilename(): void public function testRememberSetsFilename(): void
{ {
$member = Member::factory() Member::factory()
->postBillKind()
->defaults() ->defaults()
->state(['lastname' => '::lastname::']) ->state(['lastname' => '::lastname::'])
->has(Payment::factory()->notPaid()->state(['last_remembered_at' => now()->subMonths(6)])) ->has(Payment::factory()->notPaid()->state(['last_remembered_at' => now()->subMonths(6)]))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice(RememberDocument::class, $this->query($member)); $invoice = RememberDocument::fromMembers($this->query(RememberDocument::class)->getMembers()->first());
$this->assertEquals('zahlungserinnerung-fur-lastname.pdf', $invoice->compiledFilename()); $this->assertEquals('zahlungserinnerung-fur-lastname.pdf', $invoice->compiledFilename());
} }
public function testItCreatesOneFileForFamilyMembers(): void public function testItCreatesOneFileForFamilyMembers(): void
{ {
$firstMember = Member::factory() Member::factory()
->defaults() ->defaults()
->postBillKind()
->state(['firstname' => 'Max1', 'lastname' => '::lastname::', 'address' => '::address::', 'zip' => '12345', 'location' => '::location::']) ->state(['firstname' => 'Max1', 'lastname' => '::lastname::', 'address' => '::address::', 'zip' => '12345', 'location' => '::location::'])
->has(Payment::factory()->notPaid()->nr('nr1')) ->has(Payment::factory()->notPaid()->nr('nr1'))
->create(); ->create();
Member::factory() Member::factory()
->defaults() ->defaults()
->postBillKind()
->state(['firstname' => 'Max2', 'lastname' => '::lastname::', 'address' => '::address::', 'zip' => '12345', 'location' => '::location::']) ->state(['firstname' => 'Max2', 'lastname' => '::lastname::', 'address' => '::address::', 'zip' => '12345', 'location' => '::location::'])
->has(Payment::factory()->notPaid()->nr('nr2')) ->has(Payment::factory()->notPaid()->nr('nr2'))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice(BillDocument::class, $this->query($firstMember)); $this->assertCount(2, $this->query(BillDocument::class)->getMembers()->first());
$invoice->assertHasAllContent(['Max1', 'Max2', 'nr1', 'nr2']);
} }
/** /**
@ -135,12 +142,13 @@ class DocumentFactoryTest extends TestCase
'iban' => 'DE444', 'iban' => 'DE444',
'bic' => 'SOLSSSSS', 'bic' => 'SOLSSSSS',
]); ]);
$member = Member::factory() Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('nr2')) ->postBillKind()
->has(Payment::factory()->notPaid()->nr('nr2')->state(['last_remembered_at' => now()->subYear()]))
->create(); ->create();
$invoice = app(DocumentFactory::class)->singleInvoice($type, $this->query($member)); $invoice = BillDocument::fromMembers($this->query(BillDocument::class)->getMembers()->first());
$invoice->assertHasAllContent([ $invoice->assertHasAllContent([
'langer Stammesname', 'langer Stammesname',
@ -156,26 +164,11 @@ class DocumentFactoryTest extends TestCase
]); ]);
} }
public function testItGeneratesAPdf(): void /**
* @param class-string<Invoice> $type
*/
private function query(string $type): InvoiceMemberQuery
{ {
Tex::fake(); return (new BillKindQuery(BillKind::POST))->type($type);
$member = Member::factory()
->defaults()
->has(Payment::factory()->notPaid())
->create(['lastname' => 'lastname']);
$this->withoutExceptionHandling();
$this->login()->init()->loginNami();
$response = $this->call('GET', "/member/{$member->id}/pdf", [
'type' => BillDocument::class,
]);
$this->assertEquals('application/pdf', $response->headers->get('content-type'));
$this->assertEquals('inline; filename="rechnung-fur-lastname.pdf"', $response->headers->get('content-disposition'));
}
private function query(Member $member): InvoiceMemberQuery
{
return new SingleMemberQuery($member);
} }
} }

View File

@ -8,7 +8,6 @@ use App\Member\Member;
use App\Payment\Payment; use App\Payment\Payment;
use App\Payment\PaymentMail; use App\Payment\PaymentMail;
use Illuminate\Foundation\Testing\DatabaseTransactions; use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Storage;
use Tests\RequestFactories\Child; use Tests\RequestFactories\Child;
@ -19,42 +18,29 @@ class InvoiceSendActionTest extends TestCase
{ {
use DatabaseTransactions; use DatabaseTransactions;
public Member $member; public function testItCanCreatePdfPayments(): void
public function setUp(): void
{ {
parent::setUp(); Mail::fake();
Tex::spy();
Storage::fake('temp'); Storage::fake('temp');
$this->withoutExceptionHandling(); $this->withoutExceptionHandling();
$this->login()->loginNami(); $this->login()->loginNami();
$this->member = Member::factory() $member = Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [ ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [
new Child('a', 5400), new Child('a', 5400),
])) ]))
->emailBillKind() ->emailBillKind()
->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']); ->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']);
}
public function testItCanCreatePdfPayments(): void
{
Mail::fake();
Artisan::call('invoice:send');
Mail::assertSent(PaymentMail::class, fn ($mail) => Storage::disk('temp')->path('rechnung-fur-mom.pdf') === $mail->filename && Storage::disk('temp')->exists('rechnung-fur-mom.pdf'));
}
public function testItCanCompileAttachment(): void
{
Mail::fake();
Tex::spy();
InvoiceSendAction::run(); InvoiceSendAction::run();
Tex::assertCompiled(BillDocument::class, fn ($document) => 'Mom' === $document->pages->first()->familyName Mail::assertSent(PaymentMail::class, fn ($mail) => Storage::disk('temp')->path('rechnung-fur-mom.pdf') === $mail->filename && Storage::disk('temp')->exists('rechnung-fur-mom.pdf'));
&& $document->pages->first()->getPositions() === ['tollerbeitrag 1997 für Lah Mom' => '54.00'] Tex::assertCompiled(
BillDocument::class,
fn ($document) => 'Mom' === $document->familyName
&& $document->positions === ['tollerbeitrag 1997 für Lah Mom' => '54.00']
); );
Tex::assertCompiledContent(BillDocument::class, BillDocument::from($member->payments->first()->invoice_data)->renderBody());
} }
} }

View File

@ -3,7 +3,12 @@
namespace Tests\Feature\Sendpayment; namespace Tests\Feature\Sendpayment;
use App\Invoice\BillDocument; use App\Invoice\BillDocument;
use App\Invoice\BillKind;
use App\Invoice\DocumentFactory;
use App\Invoice\InvoiceSettings; use App\Invoice\InvoiceSettings;
use App\Invoice\Queries\BillKindQuery;
use App\Invoice\Queries\SingleMemberQuery;
use App\Invoice\RememberDocument;
use App\Member\Member; use App\Member\Member;
use App\Payment\Payment; use App\Payment\Payment;
use App\Payment\Status; use App\Payment\Status;
@ -30,27 +35,81 @@ class SendpaymentTest extends TestCase
$this->assertStringContainsString('BillDocument', $href); $this->assertStringContainsString('BillDocument', $href);
} }
public function testItDownloadsPdfOfAllMembersForBill(): void
{
InvoiceSettings::fake(InvoiceSettingsFake::new()->create());
$this->withoutExceptionHandling()->login()->loginNami();
Member::factory()->defaults()->postBillKind()->count(3)
->has(Payment::factory()->notPaid()->subscription('tollerbeitrag', [new Child('a', 5400)]))
->create();
$response = $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Invoice\\BillDocument']);
$response->assertOk();
$this->assertPdfPageCount(3, $response->getFile());
}
public function testItDownloadsPdfOfAllMembersForRemember(): void
{
InvoiceSettings::fake(InvoiceSettingsFake::new()->create());
$this->withoutExceptionHandling()->login()->loginNami();
Member::factory()->defaults()->postBillKind()->count(3)
->has(Payment::factory()->pending()->subscription('tollerbeitrag', [new Child('a', 5400)]))
->create();
$response = $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Invoice\\RememberDocument']);
$response->assertOk();
$this->assertPdfPageCount(3, $response->getFile());
}
public function testItCanCreatePdfPayments(): void public function testItCanCreatePdfPayments(): void
{ {
InvoiceSettings::fake(InvoiceSettingsFake::new()->create()); InvoiceSettings::fake(InvoiceSettingsFake::new()->create());
Tex::spy(); Tex::spy();
$this->withoutExceptionHandling(); $this->withoutExceptionHandling()->login()->loginNami();
$this->login()->loginNami(); $members = Member::factory()
$member = Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('a', 5400)])) ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('a', 5400)]))
->has(Payment::factory()->paid()->nr('1998')->subscription('bezahltdesc', [new Child('b', 5800)])) ->has(Payment::factory()->paid()->nr('1998')->subscription('bezahltdesc', [new Child('b', 5800)]))
->postBillKind() ->postBillKind()
->count(3)
->create(); ->create();
$member = $members->first();
$response = $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Invoice\\BillDocument']); $this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Invoice\\BillDocument']);
$response->assertOk();
$this->assertEquals(Status::firstWhere('name', 'Rechnung gestellt')->id, $member->payments->firstWhere('nr', '1997')->status_id); $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); $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']) Tex::assertCompiled(
&& $document->missesAllContent(['1998', 'bezahltdesc', '58.00']) BillDocument::class,
fn ($document) => $document->hasAllContent(['1997', 'tollerbeitrag', '54.00'])
&& $document->missesAllContent(['1998', 'bezahltdesc', '58.00'])
); );
$member->payments->firstWhere('nr', '1997')->update(['status_id' => Status::firstWhere('name', 'Nicht bezahlt')->id]);
$invoice = BillDocument::fromMembers((new BillKindQuery(BillKind::POST))->type(BillDocument::class)->getMembers()->first());
$this->assertEquals(
BillDocument::from($member->payments->firstWhere('nr', '1997')->invoice_data)->renderBody(),
$invoice->renderBody()
);
}
public function testItCanCreatePdfPaymentsForRemember(): void
{
InvoiceSettings::fake(InvoiceSettingsFake::new()->create());
Tex::spy();
$this->withoutExceptionHandling()->login()->loginNami();
$member = Member::factory()
->defaults()
->has(Payment::factory()->pending()->nr('1997')->subscription('tollerbeitrag', [new Child('a', 5400)]))
->postBillKind()
->create();
$this->call('GET', route('sendpayment.pdf'), ['type' => 'App\\Invoice\\RememberDocument']);
Tex::assertCompiled(
RememberDocument::class,
fn ($document) => $document->hasAllContent(['1997', 'tollerbeitrag', '54.00'])
);
$this->assertNull($member->payments()->first()->invoice_data);
$this->assertEquals(now()->format('Y-m-d'), $member->payments->first()->last_remembered_at->format('Y-m-d'));
} }
public function testItDoesntCreatePdfsWhenUserHasEmail(): void public function testItDoesntCreatePdfsWhenUserHasEmail(): void
@ -58,7 +117,7 @@ class SendpaymentTest extends TestCase
Tex::spy(); Tex::spy();
$this->withoutExceptionHandling(); $this->withoutExceptionHandling();
$this->login()->loginNami(); $this->login()->loginNami();
$member = Member::factory() Member::factory()
->defaults() ->defaults()
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('u', 5400)])) ->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [new Child('u', 5400)]))
->emailBillKind() ->emailBillKind()

View File

@ -11,6 +11,7 @@ use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Http;
use Illuminate\Testing\TestResponse; use Illuminate\Testing\TestResponse;
use Phake; use Phake;
use Symfony\Component\HttpFoundation\File\File;
use Tests\Lib\MakesHttpCalls; use Tests\Lib\MakesHttpCalls;
use Tests\Lib\TestsInertia; use Tests\Lib\TestsInertia;
use Zoomyboy\LaravelNami\Authentication\Auth; use Zoomyboy\LaravelNami\Authentication\Auth;
@ -81,8 +82,8 @@ abstract class TestCase extends BaseTestCase
$sessionErrors = $response->getSession()->get('errors')->getBag('default'); $sessionErrors = $response->getSession()->get('errors')->getBag('default');
foreach ($errors as $key => $value) { foreach ($errors as $key => $value) {
$this->assertTrue($sessionErrors->has($key), "Cannot find key {$key} in errors '".print_r($sessionErrors, true)); $this->assertTrue($sessionErrors->has($key), "Cannot find key {$key} in errors '" . print_r($sessionErrors, true));
$this->assertEquals($value, $sessionErrors->get($key)[0], "Failed to validate value for session error key {$key}. Actual value: ".print_r($sessionErrors, true)); $this->assertEquals($value, $sessionErrors->get($key)[0], "Failed to validate value for session error key {$key}. Actual value: " . print_r($sessionErrors, true));
} }
return $this; return $this;
@ -106,4 +107,14 @@ abstract class TestCase extends BaseTestCase
return $this; return $this;
} }
public function assertPdfPageCount(int $pageCount, File $file): void
{
$this->assertTrue(file_exists($file->getPathname()));
exec('pdfinfo ' . escapeshellarg($file->getPathname()) . ' | grep ^Pages | sed "s/Pages:\s*//"', $output, $returnVar);
$this->assertSame(0, $returnVar, 'Failed to get Pages of PDF File ' . $file->getPathname());
$this->assertCount(1, $output, 'Failed to parse output format of pdfinfo');
$this->assertEquals($pageCount, $output[0]);
}
} }