70 lines
2.4 KiB
PHP
70 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Mail;
|
|
|
|
use App\Mail\Concerns\SendsFromMailbox;
|
|
use App\Models\Invoice;
|
|
use App\Services\Billing\InvoiceRenderer;
|
|
use App\Services\Mail\MailPurpose;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Mail\Mailable;
|
|
use Illuminate\Mail\Mailables\Attachment;
|
|
use Illuminate\Mail\Mailables\Content;
|
|
use Illuminate\Mail\Mailables\Envelope;
|
|
use Illuminate\Queue\SerializesModels;
|
|
|
|
/**
|
|
* The invoice, with the invoice attached.
|
|
*
|
|
* The PDF is rendered when the mail is sent rather than fetched from anywhere,
|
|
* because nothing stores it — and rendering from the frozen snapshot means the
|
|
* attachment is the document as issued no matter when the queue gets round to
|
|
* it, or how often it is retried.
|
|
*
|
|
* From the BILLING mailbox: a reply to this is a question about money.
|
|
*/
|
|
class InvoiceMail extends Mailable implements ShouldQueue
|
|
{
|
|
use Queueable, SendsFromMailbox, SerializesModels;
|
|
|
|
public function __construct(public Invoice $invoice, public string $name) {}
|
|
|
|
public function envelope(): Envelope
|
|
{
|
|
return $this->mailboxEnvelope(
|
|
MailPurpose::BILLING,
|
|
__('invoice_mail.subject', ['number' => $this->invoice->number]),
|
|
);
|
|
}
|
|
|
|
public function content(): Content
|
|
{
|
|
$meta = (array) ($this->invoice->snapshot['meta'] ?? []);
|
|
|
|
return new Content(view: 'mail.invoice', with: [
|
|
'name' => $this->name,
|
|
'number' => $this->invoice->number,
|
|
'issuedOn' => $this->invoice->issued_on?->local()->format('d.m.Y'),
|
|
'dueOn' => $this->invoice->due_on?->local()->format('d.m.Y'),
|
|
'gross' => \App\Services\Billing\InvoiceMath::money((int) $this->invoice->gross_cents),
|
|
'currency' => $this->invoice->currency,
|
|
'paymentTerms' => (string) ($meta['payment_terms'] ?? ''),
|
|
'invoicesUrl' => route('invoices'),
|
|
]);
|
|
}
|
|
|
|
/** @return array<int, Attachment> */
|
|
public function attachments(): array
|
|
{
|
|
return [
|
|
// fromData, not fromPath: there is no path. The document exists as
|
|
// a row and becomes a file only for as long as this mail needs one.
|
|
Attachment::fromData(
|
|
fn () => app(InvoiceRenderer::class)->forInvoice($this->invoice),
|
|
$this->invoice->number.'.pdf',
|
|
)->withMime('application/pdf'),
|
|
];
|
|
}
|
|
}
|