Add payment_data for payments
This commit is contained in:
parent
fc1b647b54
commit
566ed704a6
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, '.', '');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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, '.', '');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
|
|
@ -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']));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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());
|
||||||
|
|
|
@ -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
|
85
phpstan.neon
85
phpstan.neon
|
@ -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
|
||||||
|
|
|
@ -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,15 +34,14 @@
|
||||||
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.
|
||||||
|
|
||||||
Bei Fragen zur Rechnung können Sie mich auch persönlich erreichen.
|
Bei Fragen zur Rechnung können Sie mich auch persönlich erreichen.
|
||||||
|
|
||||||
\closing{Viele Grüße \\ Der Stammesvorstand}
|
\closing{Viele Grüße \\ Der Stammesvorstand}
|
||||||
\end{letter}
|
\end{letter}
|
||||||
@endforeach
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|
||||||
|
|
|
@ -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,15 +34,14 @@
|
||||||
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.
|
||||||
|
|
||||||
Bei Fragen zur Rechnung können Sie mich auch persönlich erreichen.
|
Bei Fragen zur Rechnung können Sie mich auch persönlich erreichen.
|
||||||
|
|
||||||
\closing{Viele Grüße \\ Der Stammesvorstand}
|
\closing{Viele Grüße \\ Der Stammesvorstand}
|
||||||
\end{letter}
|
\end{letter}
|
||||||
@endforeach
|
|
||||||
\end{document}
|
\end{document}
|
||||||
|
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue