From ad4cccdcd387715056afcf423ec42001265b2d4a Mon Sep 17 00:00:00 2001 From: Matt Young Date: Fri, 30 Jan 2026 21:06:42 -0600 Subject: [PATCH] Add funcitons to email invoices. --- app/Mail/InvoiceMail.php | 37 ++++++++ .../components/⚡create-invoice.blade.php | 4 +- .../views/components/⚡edit-invoice.blade.php | 56 ++++++++++- resources/views/emails/invoice.blade.php | 93 +++++++++++++++++++ 4 files changed, 188 insertions(+), 2 deletions(-) create mode 100644 app/Mail/InvoiceMail.php create mode 100644 resources/views/emails/invoice.blade.php diff --git a/app/Mail/InvoiceMail.php b/app/Mail/InvoiceMail.php new file mode 100644 index 0000000..1ad0ca7 --- /dev/null +++ b/app/Mail/InvoiceMail.php @@ -0,0 +1,37 @@ +invoice->invoice_number} - Payment Requested", + ); + } + + public function content(): Content + { + return new Content( + view: 'emails.invoice', + with: [ + 'invoice' => $this->invoice, + 'invoiceUrl' => route('invoices.show', $this->invoice), + ], + ); + } +} \ No newline at end of file diff --git a/resources/views/components/⚡create-invoice.blade.php b/resources/views/components/⚡create-invoice.blade.php index 6b88f0d..249a663 100644 --- a/resources/views/components/⚡create-invoice.blade.php +++ b/resources/views/components/⚡create-invoice.blade.php @@ -24,7 +24,7 @@ new class extends Component { { $this->validate(); - Invoice::create([ + $invoice = Invoice::create([ 'client_id' => $this->client_id, 'status' => $this->status, 'notes' => $this->notes, @@ -34,6 +34,8 @@ new class extends Component { $this->reset(); Flux::modal('create-invoice')->close(); $this->dispatch('invoice-created'); + + $this->redirect(route('invoices.edit', $invoice), navigate: true); } #[Computed] diff --git a/resources/views/components/⚡edit-invoice.blade.php b/resources/views/components/⚡edit-invoice.blade.php index d288565..8b96ca7 100644 --- a/resources/views/components/⚡edit-invoice.blade.php +++ b/resources/views/components/⚡edit-invoice.blade.php @@ -1,12 +1,14 @@ dispatch('invoice-status-changed'); } + public function sendToPrimaryContact(): void + { + $primaryContact = $this->invoice->client->primary_contact; + + if (!$primaryContact || !$primaryContact->email) { + Flux::toast( + text: 'No primary contact with email address found.', + variant: 'danger', + ); + return; + } + + Mail::to($primaryContact->email)->send(new InvoiceMail($this->invoice)); + + if ($this->invoice->sent_at === null) { + $this->invoice->update(['sent_at' => now()]); + } + + Flux::toast( + text: "Invoice sent to {$primaryContact->full_name}.", + variant: 'success', + ); + } + + public function sendToAllContacts(): void + { + $contacts = $this->invoice->client->contacts->filter(fn($c) => $c->email); + + if ($contacts->isEmpty()) { + Flux::toast( + text: 'No contacts with email addresses found.', + variant: 'danger', + ); + return; + } + + foreach ($contacts as $contact) { + Mail::to($contact->email)->send(new InvoiceMail($this->invoice)); + } + + if ($this->invoice->sent_at === null) { + $this->invoice->update(['sent_at' => now()]); + } + + Flux::toast( + text: "Invoice sent to {$contacts->count()} contact(s).", + variant: 'success', + ); + } + #[Computed] public function clients() { @@ -90,6 +142,8 @@ new class extends Component { @elseif($this->invoice->status === InvoiceStatus::POSTED) Void Invoice Un-Post Invoice + Send to Primary Contact + Send to All Contacts @elseif($this->invoice->status === InvoiceStatus::VOID) Restore Invoice @endif diff --git a/resources/views/emails/invoice.blade.php b/resources/views/emails/invoice.blade.php new file mode 100644 index 0000000..23e3696 --- /dev/null +++ b/resources/views/emails/invoice.blade.php @@ -0,0 +1,93 @@ + + + + + + Invoice {{ $invoice->invoice_number }} + + +
+

Invoice {{ $invoice->invoice_number }}

+ +

Dear {{ $invoice->client->primary_contact?->first_name ?? 'Valued Customer' }},

+ +

Please find below a summary of your invoice from eBandroom.

+ +
+ + + + + + + + + + + + + +
Invoice Number:{{ $invoice->invoice_number }}
Invoice Date:{{ $invoice->invoice_date?->format('F j, Y') }}
Due Date:{{ $invoice->due_date?->format('F j, Y') }}
+ +
+ + + + + + + + + + @foreach($invoice->lines as $line) + + + + + @endforeach + +
DescriptionAmount
+ {{ $line->product?->name ?? $line->description }} + @if($line->description && $line->product) +
{{ $line->description }} + @endif +
${{ number_format($line->amount, 2) }}
+ +
+ + + + + + +
Total Due:${{ number_format($invoice->balance_due, 2) }}
+
+ + @if($invoice->notes) +
+ Notes:
+ {{ $invoice->notes }} +
+ @endif + +

To view your complete invoice or pay online, please click the button below:

+ +
+ View Invoice & Pay Online +
+ +

+ If you have any questions about this invoice, please don't hesitate to contact us. +

+ +

+ Thank you for your business,
+ {{ config('app.name') }} +

+
+ +
+

This email was sent regarding invoice {{ $invoice->invoice_number }}.

+
+ +