Send invoices by email.
This commit is contained in:
52
app/Http/Controllers/Api/MailController.php
Normal file
52
app/Http/Controllers/Api/MailController.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Mail\InvoiceMail;
|
||||
use App\Models\Invoice;
|
||||
use App\TenantMail;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Mailer;
|
||||
|
||||
class MailController extends Controller
|
||||
{
|
||||
/**
|
||||
* The Mailer used for sending Tenant Mails defined by options.
|
||||
* @var Mailer
|
||||
*/
|
||||
protected Mailer $mailer;
|
||||
|
||||
/**
|
||||
* Set the TenantMail::class as mailer for further usage
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->mailer = TenantMail::get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the invoice mail for the given request.
|
||||
*/
|
||||
public function sendInvoice(Request $request): JsonResponse
|
||||
{
|
||||
$invoice = Invoice::find($request->id);
|
||||
$invoiceMail = new InvoiceMail($invoice);
|
||||
$invoiceMail->subject($request->Subject);
|
||||
$invoiceMail->body = $request->Body;
|
||||
|
||||
try {
|
||||
$this->mailer->to($request->To)
|
||||
->cc($request->Cc)
|
||||
->Bcc($request->Bcc)
|
||||
->send($invoiceMail);
|
||||
} catch (\Exception $exception) {
|
||||
return response()->json(['status' => 'error', 'message' => $exception->getMessage()], 500);
|
||||
}
|
||||
|
||||
$invoice->update(['status' => 'sent']);
|
||||
|
||||
return response()->json($invoice);
|
||||
}
|
||||
}
|
||||
@@ -8,22 +8,45 @@ use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
class EController extends Controller
|
||||
{
|
||||
/**
|
||||
* Return an invoice as download for the specified resource.
|
||||
*/
|
||||
public function downloadInvoice(int $id): StreamedResponse
|
||||
{
|
||||
$invoice = Invoice::find($id);
|
||||
|
||||
$taxes = [];
|
||||
foreach ($invoice->items as $item) {
|
||||
if (!isset($taxes[$item->tax])) {
|
||||
$taxes[$item->tax] = ['tax' => 0, 'taxable' => 0];
|
||||
}
|
||||
$taxes[$item->tax]['tax'] += round($item->price * $item->amount * $item->tax / 100, 2, PHP_ROUND_HALF_UP);
|
||||
$taxes[$item->tax]['taxable'] += $item->price * $item->amount;
|
||||
}
|
||||
$taxes = self::buildTaxes($invoice->items);
|
||||
|
||||
return response()->streamDownload(function () use ($invoice, $taxes) {
|
||||
echo view('xml.invoice', ['invoice' => $invoice, 'options' => Option::optionsAsObject(), 'taxes' => $taxes]);
|
||||
}, 'test.xml', ['Content-Type' => 'application/xml']);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a rendered xml invoice as string to build an email attachment from it for the specified resource.
|
||||
*/
|
||||
public function attachInvoice(int $id): string
|
||||
{
|
||||
$invoice = Invoice::find($id);
|
||||
$taxes = self::buildTaxes($invoice->items);
|
||||
|
||||
return view('xml.invoice', ['invoice' => $invoice, 'options' => Option::optionsAsObject(), 'taxes' => $taxes])->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build taxes for the given invoice items grouped by tax.
|
||||
*/
|
||||
private static function buildTaxes($items): array
|
||||
{
|
||||
$taxes = [];
|
||||
foreach ($items as $item) {
|
||||
if (!isset($taxes[$item->tax])) {
|
||||
$taxes[$item->tax] = ['tax' => 0, 'taxable' => 0];
|
||||
}
|
||||
$taxes[$item->tax]['tax'] += round($item->price * $item->amount * $item->tax / 100, 2, PHP_ROUND_HALF_UP);
|
||||
$taxes[$item->tax]['taxable'] += $item->price * $item->amount;
|
||||
}
|
||||
|
||||
return $taxes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Contracts\View\View;
|
||||
|
||||
class InvoiceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index()
|
||||
public function index(): View
|
||||
{
|
||||
return view('invoice.index');
|
||||
}
|
||||
@@ -18,7 +18,7 @@ class InvoiceController extends Controller
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
public function create(): View
|
||||
{
|
||||
return view('invoice.create');
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class InvoiceController extends Controller
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Invoice $invoice)
|
||||
public function show(Invoice $invoice): View
|
||||
{
|
||||
return view('invoice.show', ['invoice' => $invoice]);
|
||||
}
|
||||
@@ -38,4 +38,14 @@ class InvoiceController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for sending the specified invoice.
|
||||
*/
|
||||
public function mail(int $id): View
|
||||
{
|
||||
$invoice = Invoice::find($id);
|
||||
|
||||
return view('invoice.mail', ['invoice' => $invoice]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,29 @@ use Illuminate\Http\Response;
|
||||
|
||||
class PdfController extends Controller
|
||||
{
|
||||
/**
|
||||
* Return an invoice as download for the specified resource.
|
||||
*/
|
||||
public function downloadInvoice(int $invoice_id): Response
|
||||
{
|
||||
return $this->buildInvoicePdf($invoice_id)->stream();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a rendered pdf invoice as string to build an email attachment from it for the specified resource.
|
||||
*/
|
||||
public function attachInvoice(int $invoice_id): string
|
||||
{
|
||||
return $this->buildInvoicePdf($invoice_id)->output();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the pdf view for the given invoice.
|
||||
*/
|
||||
protected function buildInvoicePdf(int $invoice_id): \Barryvdh\DomPDF\PDF
|
||||
{
|
||||
$invoice = Invoice::find($invoice_id);
|
||||
|
||||
return Pdf::loadView('pdfs.invoice', ['invoice' => $invoice->load(['address', 'delivery']), 'options' => Option::optionsAsObject()])->stream();
|
||||
return Pdf::loadView('pdfs.invoice', ['invoice' => $invoice->load(['address', 'delivery']), 'options' => Option::optionsAsObject()]);
|
||||
}
|
||||
}
|
||||
|
||||
63
app/Mail/InvoiceMail.php
Normal file
63
app/Mail/InvoiceMail.php
Normal file
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Http\Controllers\EController;
|
||||
use App\Http\Controllers\PdfController;
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Attachment;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class InvoiceMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public string $body {
|
||||
set {
|
||||
$this->body = $value;
|
||||
}
|
||||
}
|
||||
|
||||
public string $pdf;
|
||||
|
||||
public string $xml;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(protected Invoice $invoice)
|
||||
{
|
||||
$pdf = new PdfController();
|
||||
$this->pdf = $pdf->attachInvoice($invoice->id);
|
||||
|
||||
$xml = new EController();
|
||||
$this->xml = $xml->attachInvoice($invoice->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
html: 'mail.invoice.sent',
|
||||
with: ['html' => $this->body],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [
|
||||
Attachment::fromData(fn() => $this->pdf, __('invoice.Invoice') . '_' . $this->invoice->number . '.pdf')
|
||||
->withMime('application/pdf'),
|
||||
Attachment::fromData(fn() => $this->xml, __('invoice.Invoice') . '_' . $this->invoice->number . '.xml')
|
||||
->withMime('application/xml'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,20 @@ class Invoice extends Model
|
||||
*/
|
||||
protected $appends = [
|
||||
'created',
|
||||
'number'
|
||||
'number',
|
||||
'localized_state'
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the invoice state as translated string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedStateAttribute(): string
|
||||
{
|
||||
return __('invoice.state_' . $this->status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the invoice number formatted
|
||||
*/
|
||||
|
||||
32
app/TenantMail.php
Normal file
32
app/TenantMail.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use App\Http\Option;
|
||||
use Illuminate\Mail\Mailer;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class TenantMail
|
||||
{
|
||||
/**
|
||||
* Build a tenant-mailer from options
|
||||
*
|
||||
* @return Mailer
|
||||
*/
|
||||
public static function get(): Mailer
|
||||
{
|
||||
$options = Option::optionsAsObject();
|
||||
$mailer = Mail::build([
|
||||
'transport' => $options->mail_transport,
|
||||
'host' => $options->mail_host,
|
||||
'port' => $options->mail_port,
|
||||
'encryption' => (property_exists($options, 'mail_encryption')) ? $options->mail_encryption : null,
|
||||
'username' => (property_exists($options, 'mail_encryption')) ? $options->mail_username : null,
|
||||
'password' => (property_exists($options, 'mail_encryption')) ? $options->mail_password : null,
|
||||
]);
|
||||
|
||||
$mailer->alwaysFrom($options->email, $options->representative);
|
||||
|
||||
return $mailer;
|
||||
}
|
||||
}
|
||||
@@ -33,5 +33,10 @@ return [
|
||||
'Email Password Reset Link' => 'Email Passwort-Reset Link',
|
||||
'Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.' => 'Passwort vergessen? Kein Problem. Teilen Sie uns einfach Ihre E-Mail-Adresse mit und wir senden Ihnen per E-Mail einen Link zum Zurücksetzen Ihres Passworts, mit dem Sie ein neues Passwort auswählen können.',
|
||||
'Date' => 'Datum',
|
||||
'Email-To' => 'An',
|
||||
'Email-Bcc' => 'Bcc',
|
||||
'Email-Cc' => 'Cc',
|
||||
'Email-Subject' => 'Betreff',
|
||||
'Email-Body' => 'Nachricht',
|
||||
|
||||
];
|
||||
|
||||
@@ -44,5 +44,12 @@ return [
|
||||
'Color' => 'Farbe',
|
||||
'Company logo' => 'Firmenlogo',
|
||||
'Activity' => 'Tätigkeitsfeld',
|
||||
'Mail delivery' => 'E-Mail Versand',
|
||||
'Mail transport' => 'Versand Typ',
|
||||
'Mail host' => 'Server Adresse',
|
||||
'Mail port' => 'Port',
|
||||
'Mail encryption' => 'Verschlüsselung',
|
||||
'Mail username' => 'Benutzername',
|
||||
'Mail password' => 'Passwort',
|
||||
|
||||
];
|
||||
|
||||
@@ -19,5 +19,6 @@ return [
|
||||
'SaveAndContinue' => 'Speichern und Weiter',
|
||||
'Saved' => 'Gespeichert',
|
||||
'Cancel' => 'Abbrechen',
|
||||
'Send' => 'Senden',
|
||||
|
||||
];
|
||||
|
||||
@@ -43,6 +43,13 @@ return [
|
||||
'Item total price short' => 'G-Preis',
|
||||
'Net long' => 'Netto zuzüglich MwSt.',
|
||||
'Gross long' => 'Gesamtpreis inkl. gesetzlicher MwSt.',
|
||||
'Final sentence' => 'Bitte überweisen Sie den fälligen Rechnungsbetrag in Höhe von :sum bis spätestens :date auf das unten genannte Konto.'
|
||||
'Final sentence' => 'Bitte überweisen Sie den fälligen Rechnungsbetrag in Höhe von :sum bis spätestens :date auf das unten genannte Konto.',
|
||||
'state_created' => 'Erstellt',
|
||||
'state_sent' => 'Gesendet',
|
||||
'Mail' => 'E-Mail',
|
||||
'Send email to your customer with attachments.' => 'E-Mail mit Anlagen an den Kunden versenden.',
|
||||
'Invoice body' => 'Sehr geehrte Kundin, sehr geehrter Kunde\n\nim Anhang erhalten Sie die Rechnung :invoice_number.\n\nMit freundlichen Grüßen',
|
||||
'Send Pdf' => 'Rechnung als Pdf versenden',
|
||||
'Send Xml' => 'Rechnung als Xml versenden',
|
||||
|
||||
];
|
||||
|
||||
3
resources/views/components/mail-icon.blade.php
Normal file
3
resources/views/components/mail-icon.blade.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" {{ $attributes->merge(['class' => 'size-8 p-1']) }}>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 492 B |
@@ -55,18 +55,22 @@
|
||||
<div class="w-1/6" x-text="invoice.number"></div>
|
||||
<div class="w-1/6" x-text="invoice.address.name"></div>
|
||||
<div class="w-1/4" x-text="invoice.address.email"></div>
|
||||
<div class="w-1/12" x-text="invoice.status"></div>
|
||||
<div class="w-1/6 text-right" x-text="invoice.sum"></div>
|
||||
<div class="w-1/12" x-text="invoice.localized_state"></div>
|
||||
<div class="w-1/6 text-right" x-text="invoice.sum + ' €'"></div>
|
||||
<div class="w-1/6 text-right" x-text="invoice.created"></div>
|
||||
</summary>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<div class="grid grid-cols-2 border-t-2">
|
||||
<div>{{ __('invoice.Sum') }}</div>
|
||||
<div x-text="sum"></div>
|
||||
<div class="w-full border-t-2"></div>
|
||||
|
||||
<div class="w-1/2 grid grid-cols-2">
|
||||
<div>{{ __('invoice.Net') }}</div>
|
||||
<div class="text-right" x-text="(sum - tax).toFixed(2) + ' €'"></div>
|
||||
<div>{{ __('invoice.Tax') }}</div>
|
||||
<div x-text="tax"></div>
|
||||
<div class="text-right" x-text="tax + ' €'"></div>
|
||||
<div class="font-bold">{{ __('invoice.Sum') }}</div>
|
||||
<div class="font-bold text-right" x-text="sum + ' €'"></div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
113
resources/views/invoice/mail.blade.php
Normal file
113
resources/views/invoice/mail.blade.php
Normal file
@@ -0,0 +1,113 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<div class="flex flex-row w-full">
|
||||
<h2 class="grow font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('invoice.Invoice') }} {{ $invoice->number }}
|
||||
</h2>
|
||||
</div>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
|
||||
<!-- Customer data -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w" x-data="mailForm">
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="text-lg font-medium text-gray-900 dark:text-gray-100">
|
||||
{{ __('invoice.Mail') }}
|
||||
</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-400">
|
||||
{{ __("invoice.Send email to your customer with attachments.") }}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<form class="mt-6 space-y-6" @submit.prevent="">
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="To" :value="__('common.Email-To')" class="w-1/4"/>
|
||||
<x-text-input id="To" name="To" type="email" class="mt-1 block w-full"
|
||||
:value="old('To')" required multiple autocomplete="To"
|
||||
x-model="data.To"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Cc" :value="__('common.Email-Cc')" class="w-1/4"/>
|
||||
<x-text-input id="Cc" name="Cc" type="email" class="mt-1 block w-full"
|
||||
:value="old('Cc')" multiple autocomplete="Cc"
|
||||
x-model="data.Cc"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Bcc" :value="__('common.Email-Bcc')" class="w-1/4"/>
|
||||
<x-text-input id="Bcc" name="Bcc" type="email" class="mt-1 block w-full"
|
||||
:value="old('Bcc')" multiple autocomplete="Bcc"
|
||||
x-model="data.Bcc"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Subject" :value="__('common.Email-Subject')" class="w-1/4"/>
|
||||
<x-text-input id="Subject" name="Subject" type="text" class="mt-1 block w-full"
|
||||
:value="old('Subject')" required autocomplete="Subject"
|
||||
x-model="data.Subject"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Body" :value="__('common.Email-Body')" class="w-1/4"/>
|
||||
<textarea rows="10" id="Body" name="Body" class="border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm mt-1 block w-full"
|
||||
required autocomplete="Body"
|
||||
x-model="data.Body"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Pdf" :value="__('invoice.Send Pdf')" class="w-1/4"/>
|
||||
<x-text-input id="Pdf" name="Pdf" x-model="data.Pdf" type="checkbox"/>
|
||||
<x-pdf-icon class="text-gray-800 cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/pdf-download', '_blank', 'popup=true')"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label for="Xml" :value="__('invoice.Send Xml')" class="w-1/4"/>
|
||||
<x-text-input id="Xml" name="Xml" x-model="data.Xml" type="checkbox"/>
|
||||
<x-e-icon class="cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/xml-download', '_blank', 'popup=true')"/>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-4" x-show="!sent">
|
||||
<x-primary-button @click="submit">{{ __('form.Send') }}</x-primary-button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</x-app-layout>
|
||||
|
||||
<script>
|
||||
function mailForm() {
|
||||
return {
|
||||
data: {
|
||||
id: {{ $invoice->id }},
|
||||
To: '{{ $invoice->customer->email }}',
|
||||
Cc: '{{ $invoice->address->email }}',
|
||||
Bcc: '',
|
||||
Subject: '{{ __('invoice.Invoice') . ' ' . $invoice->number }}',
|
||||
Body: '{{ __('invoice.Invoice body', ['invoice_number' => $invoice->number]) }}',
|
||||
Pdf: true,
|
||||
Xml: true,
|
||||
},
|
||||
sent: false,
|
||||
|
||||
submit() {
|
||||
this.sent = true;
|
||||
axios.post('/sendInvoice', this.data)
|
||||
.then(function (response) {
|
||||
console.log(response);
|
||||
}).catch(function(error) {
|
||||
console.log(error);
|
||||
})
|
||||
console.log(this.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -5,6 +5,7 @@
|
||||
{{ __('invoice.Invoice') }} {{ $invoice->number }}
|
||||
</h2>
|
||||
<p class="relative flex flex-row">
|
||||
<x-mail-icon class="cursor-pointer mr-4" onclick="window.location.href = '{{ route('invoice.mail', $invoice->id) }}'"/>
|
||||
<x-pdf-icon class="text-gray-800 cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/pdf-download', '_blank', 'popup=true')"/>
|
||||
<x-e-icon class="cursor-pointer" onclick="window.open('/invoice/{{ $invoice->id }}/xml-download', '_blank', 'popup=true')"/>
|
||||
</p>
|
||||
|
||||
3
resources/views/mail/invoice/sent.blade.php
Normal file
3
resources/views/mail/invoice/sent.blade.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<body>
|
||||
{!! nl2br($html) !!}
|
||||
</body>
|
||||
@@ -199,7 +199,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- correspondence -->
|
||||
<!-- Correspondence -->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
@@ -233,6 +233,95 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mail derlivery -->
|
||||
<!--
|
||||
'transport' => $options->mail_transport,
|
||||
'host' => $options->mail_host,
|
||||
'port' => $options->mail_port,
|
||||
'encryption' => $options->mail_encryption,
|
||||
'username' => $options->mail_username,
|
||||
'password' => $options->mail_password,
|
||||
-->
|
||||
<div class="p-4 sm:p-8 bg-white dark:bg-gray-800 shadow sm:rounded-lg">
|
||||
<div class="max-w">
|
||||
<section>
|
||||
<details>
|
||||
<summary class="text-lg font-medium text-gray-900 dark:text-gray-100 cursor-pointer">
|
||||
{{ __('configuration.Mail delivery') }}
|
||||
</summary>
|
||||
<form class="mt-6 space-y-2" @submit.prevent="">
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_transport"
|
||||
:value="__('configuration.Mail transport')"/>
|
||||
<x-text-input id="mail_transport" name="mail_transport" type="text"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_transport')" autofocus
|
||||
placeholder="smtp"
|
||||
autocomplete="mail_transport"
|
||||
x-model="options.mail_transport"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_host"
|
||||
:value="__('configuration.Mail host')"/>
|
||||
<x-text-input id="mail_host" name="mail_host" type="text"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_host')" autofocus
|
||||
placeholder="mail.example.com"
|
||||
autocomplete="mail_host"
|
||||
x-model="options.mail_host"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_port"
|
||||
:value="__('configuration.Mail port')"/>
|
||||
<x-text-input id="mail_port" name="mail_port" type="text"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_port')" autofocus
|
||||
placeholder="587"
|
||||
autocomplete="mail_port"
|
||||
x-model="options.mail_port"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_encryption"
|
||||
:value="__('configuration.Mail encryption')"/>
|
||||
<x-text-input id="mail_encryption" name="mail_encryption" type="text"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_encryption')" autofocus
|
||||
placeholder="tls"
|
||||
autocomplete="mail_encryption"
|
||||
x-model="options.mail_encryption"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_username"
|
||||
:value="__('configuration.Mail username')"/>
|
||||
<x-text-input id="mail_username" name="mail_username" type="text"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_username')" autofocus
|
||||
placeholder="Username"
|
||||
autocomplete="mail_username"
|
||||
x-model="options.mail_username"/>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row items-center">
|
||||
<x-input-label class="w-1/4" for="mail_password"
|
||||
:value="__('configuration.Mail password')"/>
|
||||
<x-text-input id="mail_password" name="mail_password" type="password"
|
||||
class="mt-1 block w-full"
|
||||
:value="old('mail_password')" autofocus
|
||||
autocomplete="mail_password"
|
||||
x-model="options.mail_password"/>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</details>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex items-center gap-4">
|
||||
<x-primary-button @click="submit">{{ __('form.Save') }}</x-primary-button>
|
||||
</div>
|
||||
|
||||
@@ -29,6 +29,7 @@ Route::group(['as' => 'api.'], function () {
|
||||
Route::apiResource('/invoice.item', InvoiceItemController::class)->shallow();
|
||||
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
||||
Route::post('/option', [OptionController::class, 'store'])->name('option.store');
|
||||
Route::post('/sendInvoice', [\App\Http\Controllers\Api\MailController::class, 'sendInvoice'])->name('sendInvoice');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ Route::middleware('auth')->group(function () {
|
||||
Route::get('/option', [OptionController::class, 'index'])->name('option.index');
|
||||
Route::get('/invoice/{id}/pdf-download', [PdfController::class, 'downloadInvoice'])->name('invoice.pdfDownload');
|
||||
Route::get('/invoice/{id}/xml-download', [EController::class, 'downloadInvoice'])->name('invoice.eDownload');
|
||||
Route::get('/invoice/{id}/mail', [InvoiceController::class, 'mail'])->name('invoice.mail');
|
||||
});
|
||||
|
||||
require __DIR__.'/auth.php';
|
||||
|
||||
Reference in New Issue
Block a user