Compare commits
19 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
72695ea172 | |
|
|
1ef23eb45e | |
|
|
ad5bdd07fb | |
|
|
1e57833724 | |
|
|
7fc17244fd | |
|
|
eea85c9da4 | |
|
|
71c9848810 | |
|
|
8b76f59804 | |
|
|
f5a57a9d16 | |
|
|
78629989e4 | |
|
|
2640622685 | |
|
|
5143a69f52 | |
|
|
1679211f0e | |
|
|
accc3637ac | |
|
|
c70f113a07 | |
|
|
5af0457686 | |
|
|
766a1a8c2e | |
|
|
552cd34b13 | |
|
|
290d5e6cce |
|
|
@ -210,7 +210,7 @@ services:
|
|||
MYSQL_DATABASE: nextcloud
|
||||
MARIADB_AUTO_UPGRADE: 1
|
||||
- name: nextcloudserver
|
||||
image: nextcloud
|
||||
image: nextcloud:31.0.13
|
||||
environment:
|
||||
MYSQL_PASSWORD: nextcloud
|
||||
MYSQL_DATABASE: nextcloud
|
||||
|
|
|
|||
|
|
@ -17,13 +17,13 @@ trait HasValidation
|
|||
'status' => ['required', 'string', 'max:255', Rule::in(InvoiceStatus::values())],
|
||||
'via' => ['required', 'string', 'max:255', Rule::in(BillKind::values())],
|
||||
'usage' => 'required|max:255|string',
|
||||
'mail_email' => 'nullable|string|max:255|email',
|
||||
'to' => 'array',
|
||||
'to.address' => 'required|string|max:255',
|
||||
'to.location' => 'required|string|max:255',
|
||||
'to.zip' => 'required|string|max:255',
|
||||
'to.name' => 'required|string|max:255',
|
||||
'greeting' => 'required|string|max:255',
|
||||
'to.email' => 'nullable|string|max:255|email',
|
||||
'to.greeting' => 'required|string|max:255',
|
||||
'positions' => 'array',
|
||||
'positions.*.description' => 'required|string|max:300',
|
||||
'positions.*.price' => 'required|integer|min:0',
|
||||
|
|
@ -42,6 +42,8 @@ trait HasValidation
|
|||
'to.name' => 'Name',
|
||||
'to.zip' => 'PLZ',
|
||||
'to.location' => 'Ort',
|
||||
'to.greeting' => 'Anrede',
|
||||
'to.email' => 'E-Mail-Adresse',
|
||||
'status' => 'Status',
|
||||
'via' => 'Rechnungsweg',
|
||||
'usage' => 'Verwendungszweck',
|
||||
|
|
|
|||
|
|
@ -37,15 +37,15 @@ class InvoiceSendAction
|
|||
foreach (Invoice::whereNeedsBill()->where('via', BillKind::EMAIL)->get() as $invoice) {
|
||||
$document = BillDocument::fromInvoice($invoice);
|
||||
$path = Storage::disk('temp')->path(Tex::compile($document)->storeIn('', 'temp'));
|
||||
Mail::to($invoice->getMailRecipient())->send(new BillMail($invoice, $path));
|
||||
$invoice->sent($document);
|
||||
Mail::to($invoice->getRecipient())->send(new BillMail($invoice, $path));
|
||||
$document->sent($invoice);
|
||||
}
|
||||
|
||||
foreach (Invoice::whereNeedsRemember()->where('via', BillKind::EMAIL)->get() as $invoice) {
|
||||
$document = RememberDocument::fromInvoice($invoice);
|
||||
$path = Storage::disk('temp')->path(Tex::compile($document)->storeIn('', 'temp'));
|
||||
Mail::to($invoice->getMailRecipient())->send(new RememberMail($invoice, $path));
|
||||
$invoice->sent($document);
|
||||
Mail::to($invoice->getRecipient())->send(new RememberMail($invoice, $path));
|
||||
$document->sent($invoice);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -22,13 +22,13 @@ class MassPostPdfAction
|
|||
foreach (Invoice::whereNeedsBill()->where('via', BillKind::POST)->get() as $invoice) {
|
||||
$document = BillDocument::fromInvoice($invoice);
|
||||
$documents[] = $document;
|
||||
$invoice->sent($document);
|
||||
$document->sent($invoice);
|
||||
}
|
||||
|
||||
foreach (Invoice::whereNeedsRemember()->where('via', BillKind::POST)->get() as $invoice) {
|
||||
$document = RememberDocument::fromInvoice($invoice);
|
||||
$documents[] = $document;
|
||||
$invoice->sent($document);
|
||||
$document->sent($invoice);
|
||||
}
|
||||
|
||||
if (!count($documents)) {
|
||||
|
|
|
|||
|
|
@ -34,13 +34,17 @@ class MassStoreAction
|
|||
$invoices = collect([]);
|
||||
|
||||
$memberGroup = Member::payable()->get()
|
||||
->groupBy(fn ($member) => "{$member->bill_kind->value}{$member->lastname}{$member->address}{$member->zip}{$member->location}");
|
||||
->groupBy(fn($member) => "{$member->bill_kind->value}{$member->lastname}{$member->address}{$member->zip}{$member->location}");
|
||||
|
||||
foreach ($memberGroup as $members) {
|
||||
$invoice = Invoice::createForMember($members->first(), $members, $year);
|
||||
$factory = $members->first()->getInvoiceFactory()
|
||||
->withFamilyMembers($members)
|
||||
->year($year);
|
||||
|
||||
$invoice = Invoice::createFromFactory($factory);
|
||||
$invoice->save();
|
||||
$invoice->positions()->createMany($invoice->positions->toArray());
|
||||
$invoices->push($invoice->fresh('positions'));
|
||||
$invoice->positions()->createMany($invoice->getRelationValue('positions'));
|
||||
$invoices->push($invoice);
|
||||
}
|
||||
|
||||
event(new InvoicesMassStored($year, $invoices));
|
||||
|
|
|
|||
|
|
@ -30,12 +30,17 @@ class MemberNewInvoiceAction
|
|||
*/
|
||||
public function handle(Member $member, Subscription $subscription, int $year): array
|
||||
{
|
||||
$invoice = Invoice::createForMember($member, collect([$member]), $year, $subscription);
|
||||
$factory = $member->first()->getInvoiceFactory()
|
||||
->forSingleMember()
|
||||
->year($year)
|
||||
->withSubscription($subscription);
|
||||
|
||||
$invoice = Invoice::createFromFactory($factory);
|
||||
|
||||
return [
|
||||
...$invoice->getAttributes(),
|
||||
'to' => $invoice->to,
|
||||
'positions' => $invoice->getRelationValue('positions')->toArray(),
|
||||
'positions' => $invoice->getRelationValue('positions'),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
namespace App\Invoice;
|
||||
|
||||
use App\Payment\Payment;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use App\Invoice\Models\Invoice;
|
||||
|
||||
class BillDocument extends InvoiceDocument
|
||||
{
|
||||
|
|
@ -17,4 +16,8 @@ class BillDocument extends InvoiceDocument
|
|||
{
|
||||
return 'tex.invoice.bill';
|
||||
}
|
||||
|
||||
public function sent(Invoice $invoice): void {
|
||||
$invoice->sentNow();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Creators;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Data\PositionData;
|
||||
use App\Invoice\Data\ReceiverData;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
interface InvoiceCreator {
|
||||
|
||||
public function getVia(): BillKind;
|
||||
|
||||
public function getUsage(): string;
|
||||
|
||||
/** @return Collection<int, PositionData> */
|
||||
public function getPositions(): Collection;
|
||||
|
||||
public function getReceiver(): ReceiverData;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Data;
|
||||
|
||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class PositionData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $description,
|
||||
public int $price,
|
||||
#[MapOutputName('member_id')]
|
||||
public int $memberId,
|
||||
) {}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Data;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class ReceiverData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public string $address,
|
||||
public string $zip,
|
||||
public string $location,
|
||||
public ?string $email,
|
||||
public string $greeting,
|
||||
) {}
|
||||
|
||||
public static function default(): self
|
||||
{
|
||||
return new self(
|
||||
name: '',
|
||||
address: '',
|
||||
zip: '',
|
||||
location: '',
|
||||
email: null,
|
||||
greeting: ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ abstract class InvoiceDocument extends Document
|
|||
{
|
||||
abstract public function getSubject(): string;
|
||||
abstract public function view(): string;
|
||||
abstract public function sent(Invoice $invoice): void;
|
||||
|
||||
public string $until;
|
||||
public string $filename;
|
||||
|
|
@ -34,12 +35,13 @@ abstract class InvoiceDocument extends Document
|
|||
|
||||
public static function fromInvoice(Invoice $invoice): self
|
||||
{
|
||||
$recipient = $invoice->getRecipient();
|
||||
return static::factory()->withoutMagicalCreation()->from([
|
||||
'toName' => $invoice->to['name'],
|
||||
'toAddress' => $invoice->to['address'],
|
||||
'toZip' => $invoice->to['zip'],
|
||||
'toLocation' => $invoice->to['location'],
|
||||
'greeting' => $invoice->greeting,
|
||||
'toName' => $recipient->name,
|
||||
'toAddress' => $recipient->address,
|
||||
'toZip' => $recipient->zip,
|
||||
'toLocation' => $recipient->location,
|
||||
'greeting' => $recipient->greeting,
|
||||
'positions' => static::renderPositions($invoice),
|
||||
'usage' => $invoice->usage,
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -4,20 +4,18 @@ namespace App\Invoice\Models;
|
|||
|
||||
use App\Invoice\BillDocument;
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Creators\InvoiceCreator;
|
||||
use App\Invoice\Data\ReceiverData;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\InvoiceDocument;
|
||||
use App\Invoice\InvoiceSettings;
|
||||
use App\Invoice\RememberDocument;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
use Database\Factories\Invoice\Models\InvoiceFactory;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Collection;
|
||||
use Laravel\Scout\Searchable;
|
||||
use stdClass;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
|
|
@ -28,7 +26,7 @@ class Invoice extends Model
|
|||
public $guarded = [];
|
||||
|
||||
public $casts = [
|
||||
'to' => 'json',
|
||||
'to' => ReceiverData::class,
|
||||
'status' => InvoiceStatus::class,
|
||||
'via' => BillKind::class,
|
||||
'sent_at' => 'datetime',
|
||||
|
|
@ -43,38 +41,16 @@ class Invoice extends Model
|
|||
return $this->hasMany(InvoicePosition::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Member> $members
|
||||
*/
|
||||
public static function createForMember(Member $member, Collection $members, int $year, ?Subscription $subscription = null): self
|
||||
public static function createFromFactory(InvoiceCreator $factory): self
|
||||
{
|
||||
$invoice = new self([
|
||||
'to' => [
|
||||
'name' => 'Familie ' . $member->lastname,
|
||||
'address' => $member->address,
|
||||
'zip' => $member->zip,
|
||||
'location' => $member->location,
|
||||
],
|
||||
'greeting' => 'Liebe Familie ' . $member->lastname,
|
||||
'to' => $factory->getReceiver(),
|
||||
'status' => InvoiceStatus::NEW,
|
||||
'via' => $member->bill_kind,
|
||||
'usage' => 'Mitgliedsbeitrag für ' . $member->lastname,
|
||||
'mail_email' => $member->email_parents ?: $member->email,
|
||||
'via' => $factory->getVia(),
|
||||
'usage' => $factory->getUsage(),
|
||||
]);
|
||||
|
||||
$positions = collect([]);
|
||||
foreach ($members as $member) {
|
||||
$memberSubscription = $subscription ?: $member->subscription;
|
||||
foreach ($memberSubscription->children as $child) {
|
||||
$positions->push([
|
||||
'description' => str($child->name)->replace('{name}', $member->firstname . ' ' . $member->lastname)->replace('{year}', (string) $year),
|
||||
'price' => $child->amount,
|
||||
'member_id' => $member->id,
|
||||
'id' => null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
$invoice->setRelation('positions', $positions);
|
||||
$invoice->setRelation('positions', $factory->getPositions()->toArray());
|
||||
|
||||
return $invoice;
|
||||
}
|
||||
|
|
@ -121,30 +97,16 @@ class Invoice extends Model
|
|||
->where('last_remembered_at', '<=', now()->subWeeks($weeks));
|
||||
}
|
||||
|
||||
public function getMailRecipient(): stdClass
|
||||
{
|
||||
return (object) [
|
||||
'email' => $this->mail_email,
|
||||
'name' => $this->to['name']
|
||||
];
|
||||
public function getRecipient(): ReceiverData {
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
public function sent(InvoiceDocument $document): void
|
||||
{
|
||||
if (is_a($document, BillDocument::class)) {
|
||||
$this->update([
|
||||
'sent_at' => now(),
|
||||
'status' => InvoiceStatus::SENT,
|
||||
'last_remembered_at' => now(),
|
||||
]);
|
||||
}
|
||||
public function rememberedNow(): void {
|
||||
$this->update(['last_remembered_at' => now(), 'status' => InvoiceStatus::SENT]);
|
||||
}
|
||||
|
||||
if (is_a($document, RememberDocument::class)) {
|
||||
$this->update([
|
||||
'last_remembered_at' => now(),
|
||||
'status' => InvoiceStatus::SENT,
|
||||
]);
|
||||
}
|
||||
public function sentNow(): void {
|
||||
$this->update(['sent_at' => now(), 'status' => InvoiceStatus::SENT, 'last_remembered_at' => now()]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -155,10 +117,8 @@ class Invoice extends Model
|
|||
public function toSearchableArray(): array
|
||||
{
|
||||
return [
|
||||
'to' => implode(', ', $this->to),
|
||||
'to' => implode(', ', $this->getRecipient()->toArray()),
|
||||
'usage' => $this->usage,
|
||||
'greeting' => $this->greeting,
|
||||
'mail_email' => $this->mail_email,
|
||||
'status' => $this->status->value,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
namespace App\Invoice;
|
||||
|
||||
use App\Payment\Payment;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use App\Invoice\Models\Invoice;
|
||||
|
||||
class RememberDocument extends InvoiceDocument
|
||||
{
|
||||
|
|
@ -17,4 +16,8 @@ class RememberDocument extends InvoiceDocument
|
|||
{
|
||||
return 'tex.invoice.remember';
|
||||
}
|
||||
|
||||
public function sent(Invoice $invoice): void {
|
||||
$invoice->rememberedNow();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace App\Invoice\Resources;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Data\ReceiverData;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Scopes\InvoiceFilterScope;
|
||||
|
|
@ -35,9 +36,7 @@ class InvoiceResource extends JsonResource
|
|||
'status' => $this->status->value,
|
||||
'via' => $this->via->value,
|
||||
'positions' => InvoicePositionResource::collection($this->whenLoaded('positions')),
|
||||
'greeting' => $this->greeting,
|
||||
'usage' => $this->usage,
|
||||
'mail_email' => $this->mail_email,
|
||||
'links' => [
|
||||
'pdf' => route('invoice.pdf', ['invoice' => $this->getModel()]),
|
||||
'rememberpdf' => route('invoice.rememberpdf', ['invoice' => $this->getModel()]),
|
||||
|
|
@ -65,18 +64,11 @@ class InvoiceResource extends JsonResource
|
|||
'subscriptions' => Subscription::forSelect(),
|
||||
'filter' => InvoiceFilterScope::fromRequest(request()->input('filter', '')),
|
||||
'default' => [
|
||||
'to' => [
|
||||
'name' => '',
|
||||
'address' => '',
|
||||
'zip' => '',
|
||||
'location' => '',
|
||||
],
|
||||
'to' => ReceiverData::default(),
|
||||
'positions' => [],
|
||||
'greeting' => '',
|
||||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => null,
|
||||
'usage' => '',
|
||||
'mail_email' => '',
|
||||
],
|
||||
'default_position' => [
|
||||
'id' => null,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
namespace App\Member\Factories;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Creators\InvoiceCreator;
|
||||
use App\Invoice\Data\PositionData;
|
||||
use App\Invoice\Data\ReceiverData;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Subscription;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class MemberInvoiceFactory implements InvoiceCreator
|
||||
{
|
||||
|
||||
/** @var Collection<int, Member> */
|
||||
private Collection $positions;
|
||||
|
||||
private int $year;
|
||||
|
||||
private ?Subscription $subscription = null;
|
||||
|
||||
public function __construct(private Member $member) {}
|
||||
|
||||
/**
|
||||
* @param Collection<int, Member> $members
|
||||
*/
|
||||
public function withFamilyMembers(Collection $members): self
|
||||
{
|
||||
$this->positions = $members;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function forSingleMember(): self
|
||||
{
|
||||
$this->positions = collect([$this->member]);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function year(int $year): self
|
||||
{
|
||||
$this->year = $year;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withSubscription(Subscription $subscription): self
|
||||
{
|
||||
$this->subscription = $subscription;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getReceiver(): ReceiverData {
|
||||
return ReceiverData::from([
|
||||
'name' => 'Familie ' . $this->member->lastname,
|
||||
'address' => $this->member->address,
|
||||
'zip' => $this->member->zip,
|
||||
'location' => $this->member->location,
|
||||
'greeting' => 'Liebe Familie ' . $this->member->lastname,
|
||||
'email' => $this->member->email_parents ?: $this->member->email,
|
||||
]);
|
||||
}
|
||||
|
||||
public function getVia(): BillKind {
|
||||
return $this->member->bill_kind;
|
||||
}
|
||||
|
||||
public function getUsage(): string {
|
||||
return 'Mitgliedsbeitrag für ' . $this->member->lastname;
|
||||
}
|
||||
|
||||
public function getPositions(): Collection {
|
||||
/** @var Collection<int, PositionData> */
|
||||
$positions = collect([]);
|
||||
foreach ($this->positions as $member) {
|
||||
$memberSubscription = $this->subscription ?: $member->subscription;
|
||||
foreach ($memberSubscription->children as $child) {
|
||||
$positions->push(PositionData::from([
|
||||
'description' => str($child->name)->replace('{name}', $member->firstname . ' ' . $member->lastname)->replace('{year}', (string) $this->year),
|
||||
'price' => $child->amount,
|
||||
'memberId' => $member->id,
|
||||
'id' => null,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
return $positions;
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ use App\Gender;
|
|||
use App\Group;
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Models\InvoicePosition;
|
||||
use App\Member\Factories\MemberInvoiceFactory;
|
||||
use App\Nami\HasNamiField;
|
||||
use App\Nationality;
|
||||
use App\Payment\Subscription;
|
||||
|
|
@ -385,6 +386,10 @@ class Member extends Model implements Geolocatable, Preventable
|
|||
return $query->where('bill_kind', '!=', null)->where('subscription_id', '!=', null);
|
||||
}
|
||||
|
||||
public function getInvoiceFactory(): MemberInvoiceFactory {
|
||||
return new MemberInvoiceFactory($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -156,10 +156,10 @@ return [
|
|||
]
|
||||
],
|
||||
Invoice::class => [
|
||||
'filterableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'searchableAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'filterableAttributes' => ['to', 'usage', 'status', 'id'],
|
||||
'searchableAttributes' => ['to', 'usage', 'status', 'id'],
|
||||
'sortableAttributes' => [],
|
||||
'displayedAttributes' => ['to', 'usage', 'greeting', 'mail_email', 'status', 'id'],
|
||||
'displayedAttributes' => ['to', 'usage', 'status', 'id'],
|
||||
'pagination' => [
|
||||
'maxTotalHits' => 1000000,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -24,12 +24,10 @@ class InvoiceFactory extends Factory
|
|||
public function definition()
|
||||
{
|
||||
return [
|
||||
'greeting' => $this->faker->words(4, true),
|
||||
'to' => ReceiverRequestFactory::new()->create(),
|
||||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => BillKind::POST->value,
|
||||
'usage' => $this->faker->words(4, true),
|
||||
'mail_email' => $this->faker->safeEmail(),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
foreach (DB::table('invoices')->get() as $invoice) {
|
||||
$to = json_decode($invoice->to);
|
||||
$position = DB::table('invoice_positions')->where('invoice_id', $invoice->id)->first();
|
||||
|
||||
if ($position) {
|
||||
$member = DB::table('members')->where('id', $position->member_id)->first();
|
||||
if ($member) {
|
||||
$to->email = $member->email_parents ?: $member->email;
|
||||
}
|
||||
}
|
||||
|
||||
$to->greeting = 'Liebe '.$to->name;
|
||||
|
||||
DB::table('invoices')->where('id', $invoice->id)->update(['to' => json_encode($to)]);
|
||||
}
|
||||
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->dropColumn('greeting');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('invoices', function (Blueprint $table) {
|
||||
$table->string('greeting');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -41,16 +41,16 @@
|
|||
<ui-icon-button class="btn-primary self-end mb-2" icon="save" @click="saveForMember">Speichern</ui-icon-button>
|
||||
</ui-box>
|
||||
<ui-box heading=" Empfänger" container-class="grid grid-cols-2 gap-3 col-span-full">
|
||||
<f-text id="to_name" v-model="single.to.name" label="Name" class="col-span-full" required />
|
||||
<f-text id="to_address" v-model="single.to.address" label="Adresse" class="col-span-full" required />
|
||||
<f-text id="to_name" v-model="single.to.name" label="Name" required />
|
||||
<f-text id="to_address" v-model="single.to.address" label="Adresse" required />
|
||||
<f-text id="to_zip" v-model="single.to.zip" label="PLZ" required />
|
||||
<f-text id="to_location" v-model="single.to.location" label="Ort" required />
|
||||
<f-text id="mail_email" v-model="single.mail_email" label="E-Mail-Adresse" class="col-span-full" />
|
||||
<f-text id="mail_email" v-model="single.to.email" label="E-Mail-Adresse" />
|
||||
<f-text id="greeting" v-model="single.to.greeting" label="Anrede" required />
|
||||
</ui-box>
|
||||
<ui-box heading="Status" container-class="grid gap-3">
|
||||
<f-select id="status" v-model="single.status" :options="meta.statuses" name="status" label="Status" required />
|
||||
<f-select id="via" v-model="single.via" :options="meta.vias" name="via" label="Rechnungsweg" required />
|
||||
<f-text id="greeting" v-model="single.greeting" label="Anrede" required />
|
||||
<f-text id="usage" v-model="single.usage" label="Verwendungszweck" required />
|
||||
</ui-box>
|
||||
<ui-box heading="Positionen" class="col-span-full" container-class="grid gap-3">
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@component('mail::message')
|
||||
# {{ $invoice->greeting }},
|
||||
# {{ $invoice->to->greeting }},
|
||||
|
||||
Im Anhang findet ihr die aktuelle Rechnung an {{$settings->from}} für das laufende Jahr. Bitte begleicht diese bis zum angegebenen Datum.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
@component('mail::message')
|
||||
# {{ $invoice->greeting }},
|
||||
# {{ $invoice->to->greeting }},
|
||||
|
||||
Hiermit möchten wir euch an die noch ausstehenden Mitgliedsbeiträge an {{$settings->from}} für das laufende Jahr erinnern. Bitte begleicht diese bis zum angegebenen Datum.
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ it('testItDisplaysInvoices', function () {
|
|||
$invoice = Invoice::factory()
|
||||
->has(InvoicePosition::factory()->price(1100)->for($member)->state(['description' => 'lala']), 'positions')
|
||||
->has(InvoicePosition::factory()->price(2200)->withMember(), 'positions')
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla'))
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->email('a@b.de')->greeting('Liebe Fam'))
|
||||
->sentAt(now()->subDay())
|
||||
->via(BillKind::POST)
|
||||
->status(InvoiceStatus::SENT)
|
||||
|
|
@ -36,9 +36,9 @@ it('testItDisplaysInvoices', function () {
|
|||
->assertInertiaPath('data.data.0.sent_at_human', now()->subDay()->format('d.m.Y'))
|
||||
->assertInertiaPath('data.data.0.status', 'Rechnung gestellt')
|
||||
->assertInertiaPath('data.data.0.via', 'Post')
|
||||
->assertInertiaPath('data.data.0.mail_email', 'a@b.de')
|
||||
->assertInertiaPath('data.data.0.to.email', 'a@b.de')
|
||||
->assertInertiaPath('data.data.0.usage', 'Usa')
|
||||
->assertInertiaPath('data.data.0.greeting', $invoice->greeting)
|
||||
->assertInertiaPath('data.data.0.to.greeting', 'Liebe Fam')
|
||||
->assertInertiaPath('data.data.0.positions.0.price', 1100)
|
||||
->assertInertiaPath('data.data.0.positions.0.member_id', $member->id)
|
||||
->assertInertiaPath('data.data.0.positions.0.description', 'lala')
|
||||
|
|
@ -62,13 +62,13 @@ it('testItDisplaysInvoices', function () {
|
|||
'address' => '',
|
||||
'zip' => '',
|
||||
'location' => '',
|
||||
'email' => null,
|
||||
'greeting' => '',
|
||||
],
|
||||
'positions' => [],
|
||||
'greeting' => '',
|
||||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => null,
|
||||
'usage' => '',
|
||||
'mail_email' => '',
|
||||
])
|
||||
->assertInertiaPath('data.meta.default_position', [
|
||||
'id' => null,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class InvoiceRequestFactory extends RequestFactory
|
|||
{
|
||||
return [
|
||||
'to' => ReceiverRequestFactory::new(),
|
||||
'greeting' => 'Hallo Familie',
|
||||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => BillKind::EMAIL->value,
|
||||
'positions' => [],
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ class InvoiceSendActionTest extends TestCase
|
|||
Storage::fake('temp');
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$invoice = Invoice::factory()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Muster'))
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Muster')->email('max@muster.de'))
|
||||
->has(InvoicePosition::factory()->withMember(), 'positions')
|
||||
->via(BillKind::EMAIL)
|
||||
->create(['mail_email' => 'max@muster.de']);
|
||||
->create();
|
||||
|
||||
InvoiceSendAction::run();
|
||||
|
||||
|
|
@ -42,6 +42,28 @@ class InvoiceSendActionTest extends TestCase
|
|||
$this->assertEquals(now()->format('Y-m-d'), $invoice->fresh()->sent_at->format('Y-m-d'));
|
||||
}
|
||||
|
||||
public function testItRendersBillMail(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$invoice = Invoice::factory()
|
||||
->to(ReceiverRequestFactory::new()->email('max@muster.de')->greeting('Liebe Familie Doe'))
|
||||
->create();
|
||||
|
||||
$mail = new BillMail($invoice, 'file.txt');
|
||||
$mail->assertSeeInText('Liebe Familie Doe');
|
||||
}
|
||||
|
||||
public function testItRendersRememberMail(): void
|
||||
{
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$invoice = Invoice::factory()
|
||||
->to(ReceiverRequestFactory::new()->email('max@muster.de')->greeting('Liebe Familie Doe'))
|
||||
->create();
|
||||
|
||||
$mail = new RememberMail($invoice, 'file.txt');
|
||||
$mail->assertSeeInText('Liebe Familie Doe');
|
||||
}
|
||||
|
||||
public function testItRemembersInvoices(): void
|
||||
{
|
||||
Mail::fake();
|
||||
|
|
@ -50,11 +72,11 @@ class InvoiceSendActionTest extends TestCase
|
|||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
app(InvoiceSettings::class)->fill(['from_long' => 'Stammname', 'replyTo' => 'reply@mail.com'])->save();
|
||||
$invoice = Invoice::factory()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Muster'))
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Muster')->email('max@muster.de'))
|
||||
->has(InvoicePosition::factory()->withMember(), 'positions')
|
||||
->via(BillKind::EMAIL)
|
||||
->status(InvoiceStatus::SENT)
|
||||
->create(['sent_at' => now()->subMonths(6), 'mail_email' => 'max@muster.de', 'last_remembered_at' => now()->subMonths(6)]);
|
||||
->create(['sent_at' => now()->subMonths(6), 'last_remembered_at' => now()->subMonths(6)]);
|
||||
|
||||
InvoiceSendAction::run();
|
||||
|
||||
|
|
|
|||
|
|
@ -24,36 +24,32 @@ class InvoiceStoreActionTest extends TestCase
|
|||
$response = $this->postJson(
|
||||
route('invoice.store'),
|
||||
InvoiceRequestFactory::new()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen'))
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')->greeting('Hallo Familie')->email('a@b.de'))
|
||||
->status(InvoiceStatus::PAID)
|
||||
->via(BillKind::POST)
|
||||
->state([
|
||||
'greeting' => 'Hallo Familie',
|
||||
])
|
||||
->position(InvoicePositionRequestFactory::new()->description('Beitrag Abc')->price(3250)->member($member))
|
||||
->create(['mail_email' => 'a@b.de'])
|
||||
->create()
|
||||
);
|
||||
|
||||
$response->assertOk();
|
||||
$this->assertDatabaseHas('invoices', [
|
||||
'greeting' => 'Hallo Familie',
|
||||
'via' => BillKind::POST->value,
|
||||
'status' => InvoiceStatus::PAID->value,
|
||||
'mail_email' => 'a@b.de',
|
||||
]);
|
||||
$invoice = Invoice::firstWhere('greeting', 'Hallo Familie');
|
||||
$invoice = Invoice::firstWhere('to->greeting', 'Hallo Familie');
|
||||
$this->assertDatabaseHas('invoice_positions', [
|
||||
'invoice_id' => $invoice->id,
|
||||
'member_id' => $member->id,
|
||||
'price' => 3250,
|
||||
'description' => 'Beitrag Abc',
|
||||
]);
|
||||
$this->assertEquals([
|
||||
'name' => 'Familie Blabla',
|
||||
'address' => 'Musterstr 44',
|
||||
'zip' => '22222',
|
||||
'location' => 'Solingen',
|
||||
], $invoice->to);
|
||||
|
||||
$this->assertEquals('Familie Blabla', $invoice->to->name);
|
||||
$this->assertEquals('Musterstr 44', $invoice->to->address);
|
||||
$this->assertEquals('22222', $invoice->to->zip);
|
||||
$this->assertEquals('Solingen', $invoice->to->location);
|
||||
$this->assertEquals('Hallo Familie', $invoice->to->greeting);
|
||||
$this->assertEquals('a@b.de', $invoice->to->email);
|
||||
}
|
||||
|
||||
public static function validationDataProvider(): Generator
|
||||
|
|
|
|||
|
|
@ -25,29 +25,27 @@ class InvoiceUpdateActionTest extends TestCase
|
|||
$this->patchJson(
|
||||
route('invoice.update', ['invoice' => $invoice]),
|
||||
InvoiceRequestFactory::new()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen'))
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Blabla')->address('Musterstr 44')->zip('22222')->location('Solingen')->greeting('Hallo Familie Blabla'))
|
||||
->status(InvoiceStatus::PAID)
|
||||
->via(BillKind::POST)
|
||||
->state([
|
||||
'greeting' => 'Hallo Familie',
|
||||
])
|
||||
->create()
|
||||
)->assertOk();
|
||||
|
||||
$this->assertDatabaseCount('invoices', 1);
|
||||
$this->assertDatabaseHas('invoices', [
|
||||
'greeting' => 'Hallo Familie',
|
||||
'via' => BillKind::POST->value,
|
||||
'status' => InvoiceStatus::PAID->value,
|
||||
'id' => $invoice->id,
|
||||
]);
|
||||
$invoice = Invoice::firstWhere('greeting', 'Hallo Familie');
|
||||
$this->assertEquals([
|
||||
'name' => 'Familie Blabla',
|
||||
'address' => 'Musterstr 44',
|
||||
'zip' => '22222',
|
||||
'location' => 'Solingen',
|
||||
], $invoice->to);
|
||||
$invoice = Invoice::firstWhere('to->greeting', 'Hallo Familie Blabla');
|
||||
$this->assertEquals('Familie Blabla', $invoice->to->name);
|
||||
$this->assertEquals('Musterstr 44', $invoice->to->address);
|
||||
$this->assertEquals('22222', $invoice->to->zip);
|
||||
$this->assertEquals('Solingen', $invoice->to->location);
|
||||
$this->assertEquals('Hallo Familie Blabla', $invoice->to->greeting);
|
||||
$this->assertNull($invoice->to->email);
|
||||
}
|
||||
|
||||
public function testItAddsAPosition(): void
|
||||
|
|
|
|||
|
|
@ -57,14 +57,13 @@ class MassStoreActionTest extends TestCase
|
|||
|
||||
$invoice = Invoice::first();
|
||||
$this->assertNotNull($invoice);
|
||||
$this->assertEquals([
|
||||
'name' => 'Familie Muster',
|
||||
'address' => 'Maxstr 4',
|
||||
'zip' => '33445',
|
||||
'location' => 'Solingen',
|
||||
], $invoice->to);
|
||||
$this->assertEquals('Familie Muster', $invoice->to->name);
|
||||
$this->assertEquals('Maxstr 4', $invoice->to->address);
|
||||
$this->assertEquals('33445', $invoice->to->zip);
|
||||
$this->assertEquals('Solingen', $invoice->to->location);
|
||||
$this->assertEquals('Liebe Familie Muster', $invoice->to->greeting);
|
||||
$this->assertEquals('lala@b.de', $invoice->to->email);
|
||||
$this->assertEquals('Mitgliedsbeitrag für Muster', $invoice->usage);
|
||||
$this->assertEquals('lala@b.de', $invoice->mail_email);
|
||||
$this->assertEquals(BillKind::EMAIL, $invoice->via);
|
||||
$this->assertDatabaseHas('invoice_positions', [
|
||||
'invoice_id' => $invoice->id,
|
||||
|
|
|
|||
|
|
@ -32,14 +32,14 @@ class MemberNewInvoiceActionTest extends TestCase
|
|||
|
||||
$this->post(route('invoice.new-invoice-attributes'), ['member_id' => $member->id, 'year' => 2019, 'subscription_id' => $subscription->id])
|
||||
->assertOk()
|
||||
->assertJsonPath('greeting', 'Liebe Familie Muster')
|
||||
->assertJsonPath('to.greeting', 'Liebe Familie Muster')
|
||||
->assertJsonPath('to.address', 'Maxstr 4')
|
||||
->assertJsonPath('to.location', 'Solingen')
|
||||
->assertJsonPath('to.zip', '33445')
|
||||
->assertJsonPath('to.name', 'Familie Muster')
|
||||
->assertJsonPath('to.email', 'lala@b.de')
|
||||
->assertJsonPath('usage', 'Mitgliedsbeitrag für Muster')
|
||||
->assertJsonPath('via', 'E-Mail')
|
||||
->assertJsonPath('mail_email', 'lala@b.de')
|
||||
->assertJsonPath('status', 'Neu')
|
||||
->assertJsonPath('positions.0.description', 'beitrag Max Muster')
|
||||
->assertJsonPath('positions.0.member_id', $member->id)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ class ReceiverRequestFactory extends RequestFactory
|
|||
'address' => 'Musterstr 44',
|
||||
'zip' => '22222',
|
||||
'location' => 'Solingen',
|
||||
'greeting' => 'Liebe Familie Blabla',
|
||||
'email' => null,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -35,4 +37,14 @@ class ReceiverRequestFactory extends RequestFactory
|
|||
{
|
||||
return $this->state(['location' => $location]);
|
||||
}
|
||||
|
||||
public function greeting(string $greeting): self
|
||||
{
|
||||
return $this->state(['greeting' => $greeting]);
|
||||
}
|
||||
|
||||
public function email(string $email): self
|
||||
{
|
||||
return $this->state(['email' => $email]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue