Add funcitons to email invoices.
This commit is contained in:
parent
3c96f639fa
commit
ad4cccdcd3
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class InvoiceMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
public Invoice $invoice
|
||||
) {}
|
||||
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
subject: "Invoice {$this->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),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -1,12 +1,14 @@
|
|||
<?php
|
||||
|
||||
use App\Enums\InvoiceStatus;
|
||||
use App\Mail\InvoiceMail;
|
||||
use App\Models\Client;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Livewire\Component;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\Attributes\Validate;
|
||||
|
||||
use Flux\Flux;
|
||||
|
||||
new class extends Component {
|
||||
public $invoice;
|
||||
|
|
@ -72,6 +74,56 @@ new class extends Component {
|
|||
$this->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)
|
||||
<flux:button variant="primary" color="red" wire:click="setStatus('void')">Void Invoice</flux:button>
|
||||
<flux:button variant="primary" color="amber" wire:click="setStatus('draft')">Un-Post Invoice</flux:button>
|
||||
<flux:button variant="primary" wire:click="sendToPrimaryContact" wire:loading.attr="disabled">Send to Primary Contact</flux:button>
|
||||
<flux:button variant="primary" wire:click="sendToAllContacts" wire:loading.attr="disabled">Send to All Contacts</flux:button>
|
||||
@elseif($this->invoice->status === InvoiceStatus::VOID)
|
||||
<flux:button variant="primary" color="blue" wire:click="setStatus('draft')">Restore Invoice</flux:button>
|
||||
@endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Invoice {{ $invoice->invoice_number }}</title>
|
||||
</head>
|
||||
<body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; line-height: 1.6; color: #333; max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<div style="background-color: #f8f9fa; padding: 30px; border-radius: 8px;">
|
||||
<h1 style="color: #1a1a1a; margin-top: 0; font-size: 24px;">Invoice {{ $invoice->invoice_number }}</h1>
|
||||
|
||||
<p style="margin-bottom: 20px;">Dear {{ $invoice->client->primary_contact?->first_name ?? 'Valued Customer' }},</p>
|
||||
|
||||
<p>Please find below a summary of your invoice from eBandroom.</p>
|
||||
|
||||
<div style="background-color: #ffffff; padding: 20px; border-radius: 6px; margin: 20px 0; border: 1px solid #e9ecef;">
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 8px 0; color: #666;">Invoice Number:</td>
|
||||
<td style="padding: 8px 0; text-align: right; font-weight: 600;">{{ $invoice->invoice_number }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px 0; color: #666;">Invoice Date:</td>
|
||||
<td style="padding: 8px 0; text-align: right;">{{ $invoice->invoice_date?->format('F j, Y') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="padding: 8px 0; color: #666;">Due Date:</td>
|
||||
<td style="padding: 8px 0; text-align: right;">{{ $invoice->due_date?->format('F j, Y') }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr style="border: none; border-top: 1px solid #e9ecef; margin: 15px 0;">
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="border-bottom: 1px solid #e9ecef;">
|
||||
<th style="padding: 10px 0; text-align: left; color: #666; font-weight: 500;">Description</th>
|
||||
<th style="padding: 10px 0; text-align: right; color: #666; font-weight: 500;">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($invoice->lines as $line)
|
||||
<tr>
|
||||
<td style="padding: 10px 0;">
|
||||
{{ $line->product?->name ?? $line->description }}
|
||||
@if($line->description && $line->product)
|
||||
<br><span style="color: #666; font-size: 14px;">{{ $line->description }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td style="padding: 10px 0; text-align: right;">${{ number_format($line->amount, 2) }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<hr style="border: none; border-top: 2px solid #e9ecef; margin: 15px 0;">
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td style="padding: 8px 0; font-weight: 600; font-size: 18px;">Total Due:</td>
|
||||
<td style="padding: 8px 0; text-align: right; font-weight: 600; font-size: 18px; color: #2563eb;">${{ number_format($invoice->balance_due, 2) }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($invoice->notes)
|
||||
<div style="background-color: #fff3cd; padding: 15px; border-radius: 6px; margin: 20px 0; border-left: 4px solid #ffc107;">
|
||||
<strong>Notes:</strong><br>
|
||||
{{ $invoice->notes }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<p style="margin: 25px 0;">To view your complete invoice or pay online, please click the button below:</p>
|
||||
|
||||
<div style="text-align: center; margin: 30px 0;">
|
||||
<a href="{{ $invoiceUrl }}" style="display: inline-block; background-color: #2563eb; color: #ffffff; padding: 14px 28px; text-decoration: none; border-radius: 6px; font-weight: 600;">View Invoice & Pay Online</a>
|
||||
</div>
|
||||
|
||||
<p style="color: #666; font-size: 14px; margin-top: 30px;">
|
||||
If you have any questions about this invoice, please don't hesitate to contact us.
|
||||
</p>
|
||||
|
||||
<p style="margin-bottom: 0;">
|
||||
Thank you for your business,<br>
|
||||
<strong>{{ config('app.name') }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; padding: 20px; color: #999; font-size: 12px;">
|
||||
<p>This email was sent regarding invoice {{ $invoice->invoice_number }}.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue