Fix Send invoices
This commit is contained in:
parent
68a654494d
commit
be29a284d5
|
@ -17,6 +17,8 @@ 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_name' => 'nullable|string|max:255',
|
||||
'mail_email' => 'nullable|string|max:255|email',
|
||||
'to' => 'array',
|
||||
'to.address' => 'required|string|max:255',
|
||||
'to.location' => 'required|string|max:255',
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
|
||||
namespace App\Invoice\Actions;
|
||||
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\DocumentFactory;
|
||||
use App\Invoice\Queries\BillKindQuery;
|
||||
use App\Payment\Payment;
|
||||
use App\Payment\PaymentMail;
|
||||
use App\Invoice\BillDocument;
|
||||
use App\Invoice\Mails\BillMail;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
|
@ -33,15 +31,11 @@ class InvoiceSendAction
|
|||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
foreach (app(DocumentFactory::class)->getTypes() as $type) {
|
||||
$memberCollection = (new BillKindQuery(BillKind::EMAIL))->type($type)->getMembers();
|
||||
|
||||
foreach ($memberCollection as $members) {
|
||||
$invoice = $type::fromMembers($members);
|
||||
$invoicePath = Storage::disk('temp')->path(Tex::compile($invoice)->storeIn('', 'temp'));
|
||||
Mail::to($invoice->getRecipient())->send(new PaymentMail($invoice, $invoicePath));
|
||||
app(DocumentFactory::class)->afterSingle($invoice, $members);
|
||||
}
|
||||
foreach (Invoice::whereNeedsBill()->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);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -35,11 +35,6 @@ class BillDocument extends InvoiceDocument
|
|||
]);
|
||||
}
|
||||
|
||||
public function getMailSubject(): string
|
||||
{
|
||||
return 'Jahresrechnung';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HasMany<Payment> $query
|
||||
*
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice;
|
||||
|
||||
use App\Member\Member;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class DocumentFactory
|
||||
{
|
||||
/**
|
||||
* @var array<int, class-string<Invoice>>
|
||||
*/
|
||||
private array $types = [
|
||||
BillDocument::class,
|
||||
RememberDocument::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @return Collection<int, class-string<Invoice>>
|
||||
*/
|
||||
public function getTypes(): Collection
|
||||
{
|
||||
return collect($this->types);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Collection<(int|string), Member> $members
|
||||
*/
|
||||
public function afterSingle(Invoice $invoice, Collection $members): void
|
||||
{
|
||||
foreach ($members as $member) {
|
||||
foreach ($member->payments as $payment) {
|
||||
$invoice->afterSingle($payment);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ namespace App\Invoice;
|
|||
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Payment\Payment;
|
||||
use Exception;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Support\Str;
|
||||
use Zoomyboy\Tex\Document;
|
||||
|
@ -90,25 +89,6 @@ abstract class InvoiceDocument extends Document
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getRecipient(): MailRecipient
|
||||
{
|
||||
throw_unless($this->email, Exception::class, 'Cannot get Recipient. Mail not set.');
|
||||
|
||||
return new MailRecipient($this->email, $this->familyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return view-string
|
||||
*/
|
||||
public function mailView(): string
|
||||
{
|
||||
$view = 'mail.payment.' . Str::snake(class_basename($this));
|
||||
|
||||
throw_unless(view()->exists($view), Exception::class, 'Mail view ' . $view . ' existiert nicht.');
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Invoice\Mails;
|
||||
|
||||
use App\Invoice\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class BillMail extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(public Invoice $invoice, public string $filename)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->markdown('mail.invoice.bill')
|
||||
->attach($this->filename)
|
||||
->replyTo('kasse@stamm-silva.de')
|
||||
->subject('Rechnung | DPSG Stamm Silva');
|
||||
}
|
||||
}
|
|
@ -2,13 +2,16 @@
|
|||
|
||||
namespace App\Invoice\Models;
|
||||
|
||||
use App\Invoice\BillDocument;
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\InvoiceDocument;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use stdClass;
|
||||
|
||||
class Invoice extends Model
|
||||
{
|
||||
|
@ -48,6 +51,8 @@ class Invoice extends Model
|
|||
'status' => InvoiceStatus::NEW,
|
||||
'via' => $member->bill_kind,
|
||||
'usage' => 'Mitgliedsbeitrag für ' . $member->lastname,
|
||||
'mail_email' => $member->email,
|
||||
'mail_name' => 'Familie ' . $member->lastname,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -67,4 +72,32 @@ class Invoice extends Model
|
|||
{
|
||||
return $query->whereIn('status', [InvoiceStatus::NEW->value, InvoiceStatus::SENT->value]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Builder<self> $query
|
||||
*
|
||||
* @return Builder<self>
|
||||
*/
|
||||
public function scopeWhereNeedsBill(Builder $query): Builder
|
||||
{
|
||||
return $query->where('status', InvoiceStatus::NEW);
|
||||
}
|
||||
|
||||
public function getMailRecipient(): stdClass
|
||||
{
|
||||
return (object) [
|
||||
'email' => $this->mail_email,
|
||||
'name' => $this->mail_name,
|
||||
];
|
||||
}
|
||||
|
||||
public function sent(InvoiceDocument $document): void
|
||||
{
|
||||
if (is_a($document, BillDocument::class)) {
|
||||
$this->update([
|
||||
'sent_at' => now(),
|
||||
'status' => InvoiceStatus::SENT,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Invoice\Queries;
|
||||
|
||||
use App\Invoice\Invoice;
|
||||
use App\Invoice\InvoiceDocument;
|
||||
use App\Member\Member;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
|
@ -12,7 +12,7 @@ use Illuminate\Support\Str;
|
|||
abstract class InvoiceMemberQuery
|
||||
{
|
||||
/**
|
||||
* @param class-string<Invoice> $type
|
||||
* @param class-string<InvoiceDocument> $type
|
||||
*/
|
||||
public string $type;
|
||||
|
||||
|
@ -34,7 +34,7 @@ abstract class InvoiceMemberQuery
|
|||
}
|
||||
|
||||
/**
|
||||
* @param class-string<Invoice> $type
|
||||
* @param class-string<InvoiceDocument> $type
|
||||
*/
|
||||
public function type(string $type): self
|
||||
{
|
||||
|
|
|
@ -32,11 +32,6 @@ class RememberDocument extends InvoiceDocument
|
|||
$payment->update(['last_remembered_at' => now()]);
|
||||
}
|
||||
|
||||
public function getMailSubject(): string
|
||||
{
|
||||
return 'Zahlungserinnerung';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param HasMany<Payment> $query
|
||||
*
|
||||
|
|
|
@ -35,6 +35,8 @@ class InvoiceResource extends JsonResource
|
|||
'positions' => InvoicePositionResource::collection($this->whenLoaded('positions')),
|
||||
'greeting' => $this->greeting,
|
||||
'usage' => $this->usage,
|
||||
'mail_name' => $this->mail_name,
|
||||
'mail_email' => $this->mail_email,
|
||||
'links' => [
|
||||
'pdf' => route('invoice.pdf', ['invoice' => $this->getModel()]),
|
||||
'rememberpdf' => route('invoice.rememberpdf', ['invoice' => $this->getModel()]),
|
||||
|
@ -69,6 +71,8 @@ class InvoiceResource extends JsonResource
|
|||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => null,
|
||||
'usage' => '',
|
||||
'mail_name' => '',
|
||||
'mail_email' => '',
|
||||
],
|
||||
'default_position' => [
|
||||
'id' => null,
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace App\Payment;
|
||||
|
||||
use App\Invoice\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class PaymentMail extends Mailable
|
||||
{
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public Invoice $invoice;
|
||||
public string $filename;
|
||||
public string $salutation;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Invoice $invoice, string $filename)
|
||||
{
|
||||
$this->invoice = $invoice;
|
||||
$this->filename = $filename;
|
||||
$this->salutation = 'Liebe Familie ' . $invoice->familyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->markdown($this->invoice->mailView())
|
||||
->attach($this->filename)
|
||||
->replyTo('kasse@stamm-silva.de')
|
||||
->subject($this->invoice->getSubject() . ' | DPSG Stamm Silva');
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ return new class extends Migration
|
|||
$table->date('sent_at')->nullable();
|
||||
$table->string('via');
|
||||
$table->string('usage');
|
||||
$table->string('mail_name')->nullable();
|
||||
$table->string('mail_email')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
|
|
|
@ -492,11 +492,6 @@ parameters:
|
|||
count: 1
|
||||
path: app/Contribution/ContributionFactory.php
|
||||
|
||||
-
|
||||
message: "#^Return type of call to method Illuminate\\\\Support\\\\Collection\\<int,class\\-string\\<App\\\\Invoice\\\\Invoice\\>\\>\\:\\:map\\(\\) contains unresolvable type\\.$#"
|
||||
count: 1
|
||||
path: app/Payment/ActionFactory.php
|
||||
|
||||
-
|
||||
message: "#^Return type of call to method Illuminate\\\\Support\\\\Collection\\<int,class\\-string\\<App\\\\Setting\\\\LocalSettings\\>\\>\\:\\:map\\(\\) contains unresolvable type\\.$#"
|
||||
count: 1
|
||||
|
|
|
@ -26,7 +26,7 @@ class InvoiceIndexActionTest extends TestCase
|
|||
->sentAt(now()->subDay())
|
||||
->via(BillKind::POST)
|
||||
->status(InvoiceStatus::SENT)
|
||||
->create(['usage' => 'Usa']);
|
||||
->create(['usage' => 'Usa', 'mail_name' => 'lala', 'mail_email' => 'a@b.de']);
|
||||
|
||||
$this->get(route('invoice.index'))
|
||||
->assertInertiaPath('data.data.0.to.name', 'Familie Blabla')
|
||||
|
@ -35,6 +35,8 @@ class InvoiceIndexActionTest extends TestCase
|
|||
->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_name', 'lala')
|
||||
->assertInertiaPath('data.data.0.mail_email', 'a@b.de')
|
||||
->assertInertiaPath('data.data.0.usage', 'Usa')
|
||||
->assertInertiaPath('data.data.0.greeting', $invoice->greeting)
|
||||
->assertInertiaPath('data.data.0.positions.0.price', 1100)
|
||||
|
@ -62,6 +64,8 @@ class InvoiceIndexActionTest extends TestCase
|
|||
'status' => InvoiceStatus::NEW->value,
|
||||
'via' => null,
|
||||
'usage' => '',
|
||||
'mail_name' => '',
|
||||
'mail_email' => '',
|
||||
])
|
||||
->assertInertiaPath('data.meta.default_position', [
|
||||
'id' => null,
|
||||
|
|
|
@ -4,13 +4,14 @@ namespace Tests\Feature\Invoice;
|
|||
|
||||
use App\Invoice\Actions\InvoiceSendAction;
|
||||
use App\Invoice\BillDocument;
|
||||
use App\Member\Member;
|
||||
use App\Payment\Payment;
|
||||
use App\Payment\PaymentMail;
|
||||
use App\Invoice\BillKind;
|
||||
use App\Invoice\Enums\InvoiceStatus;
|
||||
use App\Invoice\Mails\BillMail;
|
||||
use App\Invoice\Models\Invoice;
|
||||
use App\Invoice\Models\InvoicePosition;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Tests\RequestFactories\Child;
|
||||
use Tests\TestCase;
|
||||
use Zoomyboy\Tex\Tex;
|
||||
|
||||
|
@ -23,24 +24,18 @@ class InvoiceSendActionTest extends TestCase
|
|||
Mail::fake();
|
||||
Tex::spy();
|
||||
Storage::fake('temp');
|
||||
$this->withoutExceptionHandling();
|
||||
$this->login()->loginNami();
|
||||
$member = Member::factory()
|
||||
->defaults()
|
||||
->has(Payment::factory()->notPaid()->nr('1997')->subscription('tollerbeitrag', [
|
||||
new Child('a', 5400),
|
||||
]))
|
||||
->emailBillKind()
|
||||
->create(['firstname' => 'Lah', 'lastname' => 'Mom', 'email' => 'peter@example.com']);
|
||||
$this->withoutExceptionHandling()->login()->loginNami();
|
||||
$invoice = Invoice::factory()
|
||||
->to(ReceiverRequestFactory::new()->name('Familie Muster'))
|
||||
->has(InvoicePosition::factory()->description('lalab')->withMember(), 'positions')
|
||||
->via(BillKind::EMAIL)
|
||||
->create(['mail_name' => 'Muster', 'mail_email' => 'max@muster.de']);
|
||||
|
||||
InvoiceSendAction::run();
|
||||
|
||||
Mail::assertSent(PaymentMail::class, fn ($mail) => Storage::disk('temp')->path('rechnung-fur-mom.pdf') === $mail->filename && Storage::disk('temp')->exists('rechnung-fur-mom.pdf'));
|
||||
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());
|
||||
Mail::assertSent(BillMail::class, fn ($mail) => $mail->build() && $mail->hasTo('max@muster.de', 'Muster') && Storage::disk('temp')->path('rechnung-fur-familie-muster.pdf') === $mail->filename && Storage::disk('temp')->exists('rechnung-fur-familie-muster.pdf'));
|
||||
Tex::assertCompiled(BillDocument::class, fn ($document) => 'Familie Muster' === $document->toName);
|
||||
$this->assertEquals(InvoiceStatus::SENT, $invoice->fresh()->status);
|
||||
$this->assertEquals(now()->format('Y-m-d'), $invoice->fresh()->sent_at->format('Y-m-d'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class InvoiceStoreActionTest extends TestCase
|
|||
'greeting' => 'Hallo Familie',
|
||||
])
|
||||
->position(InvoicePositionRequestFactory::new()->description('Beitrag Abc')->price(3250)->member($member))
|
||||
->create()
|
||||
->create(['mail_email' => 'a@b.de', 'mail_name' => 'lala'])
|
||||
);
|
||||
|
||||
$response->assertOk();
|
||||
|
@ -38,6 +38,8 @@ class InvoiceStoreActionTest extends TestCase
|
|||
'greeting' => 'Hallo Familie',
|
||||
'via' => BillKind::POST->value,
|
||||
'status' => InvoiceStatus::PAID->value,
|
||||
'mail_email' => 'a@b.de',
|
||||
'mail_name' => 'lala'
|
||||
]);
|
||||
$invoice = Invoice::firstWhere('greeting', 'Hallo Familie');
|
||||
$this->assertDatabaseHas('invoice_positions', [
|
||||
|
|
|
@ -49,7 +49,7 @@ class MassStoreActionTest extends TestCase
|
|||
->for(Subscription::factory()->children([
|
||||
new Child('beitrag {name}', 4466),
|
||||
new Child('beitrag2 für {name} für {year}', 2290),
|
||||
]))->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster', 'address' => 'Maxstr 4', 'zip' => '33445', 'location' => 'Solingen']);
|
||||
]))->emailBillKind()->create(['firstname' => 'Max', 'lastname' => 'Muster', 'address' => 'Maxstr 4', 'zip' => '33445', 'location' => 'Solingen', 'email' => 'lala@b.de']);
|
||||
|
||||
$this->postJson(route('invoice.mass-store'), [
|
||||
'year' => now()->addYear()->year,
|
||||
|
@ -64,6 +64,8 @@ class MassStoreActionTest extends TestCase
|
|||
'location' => 'Solingen',
|
||||
], $invoice->to);
|
||||
$this->assertEquals('Mitgliedsbeitrag für Muster', $invoice->usage);
|
||||
$this->assertEquals('lala@b.de', $invoice->mail_email);
|
||||
$this->assertEquals('Familie Muster', $invoice->mail_name);
|
||||
$this->assertEquals(BillKind::EMAIL, $invoice->via);
|
||||
$this->assertDatabaseHas('invoice_positions', [
|
||||
'invoice_id' => $invoice->id,
|
||||
|
|
Loading…
Reference in New Issue