Add reminder mail template #12
This commit is contained in:
@@ -46,7 +46,7 @@ public function index()
|
||||
});
|
||||
}
|
||||
|
||||
public function single($id)
|
||||
public static function single($id)
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'customer.contacts' => function ($query) {
|
||||
@@ -61,30 +61,18 @@ public function single($id)
|
||||
$invoiceArray = $invoice->toArray();
|
||||
$invoiceArray['customer']['payment_terms'] = $invoice->customer->paymentTerms->toArray();
|
||||
unset($invoiceArray['customer']['payment_terms_id']);
|
||||
|
||||
return ApiDataTransformer::snakeToCamel($invoiceArray);
|
||||
}
|
||||
|
||||
|
||||
public function preview($id)
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'customer.contacts' => function ($query) {
|
||||
$query->orderBy('is_primary', 'desc');
|
||||
},
|
||||
'items' => function ($query) {
|
||||
$query->orderBy('position', 'asc');
|
||||
},
|
||||
'customer.paymentTerms'
|
||||
])->findOrFail($id);
|
||||
|
||||
$invoiceArray = $invoice->toArray();
|
||||
$invoiceArray['customer']['payment_terms'] = $invoice->customer->paymentTerms->toArray();
|
||||
unset($invoiceArray['customer']['payment_terms_id']);
|
||||
|
||||
$giroCode = $this->generateGiroCode($invoice->totalAmount, $invoice->nr, $invoice->title);
|
||||
$invoice = self::single($id);
|
||||
$giroCode = $this->generateGiroCode($invoice['totalAmount'], $invoice['nr'], $invoice['title']);
|
||||
|
||||
return view('invoice', [
|
||||
'invoice' => ApiDataTransformer::snakeToCamel($invoiceArray),
|
||||
'invoice' => $invoice,
|
||||
'isPDF' => false,
|
||||
'fontPath' => '/storage/fonts',
|
||||
'giroCode' => $giroCode,
|
||||
@@ -94,24 +82,11 @@ public function preview($id)
|
||||
// https://www.itsolutionstuff.com/post/laravel-dompdf-table-with-page-break-exampleexample.html
|
||||
public function exportPdf($id)
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'customer.contacts' => function ($query) {
|
||||
$query->orderBy('is_primary', 'desc');
|
||||
},
|
||||
'items' => function ($query) {
|
||||
$query->orderBy('position', 'asc');
|
||||
},
|
||||
'customer.paymentTerms'
|
||||
])->findOrFail($id);
|
||||
|
||||
$invoiceArray = $invoice->toArray();
|
||||
$invoiceArray['customer']['payment_terms'] = $invoice->customer->paymentTerms->toArray();
|
||||
unset($invoiceArray['customer']['payment_terms_id']);
|
||||
|
||||
$giroCode = $this->generateGiroCode($invoice->totalAmount, $invoice->nr, $invoice->title);
|
||||
$invoice = self::single($id);
|
||||
$giroCode = $this->generateGiroCode($invoice['totalAmount'], $invoice['nr'], $invoice['title']);
|
||||
|
||||
$pdf = Pdf::loadView('invoice', [
|
||||
'invoice' => ApiDataTransformer::snakeToCamel($invoiceArray),
|
||||
'invoice' => $invoice,
|
||||
'isPDF' => true,
|
||||
'fontPath' => public_path('storage/fonts/'),
|
||||
'giroCode' => $giroCode,
|
||||
@@ -119,25 +94,14 @@ public function exportPdf($id)
|
||||
$pdf->setOption(['isRemoteEnabled' => true]);
|
||||
$pdf->setOption(['fontDir' => public_path('storage/fonts/')]);
|
||||
$pdf->setOption(['fontCache' => public_path('storage/fonts/')]);
|
||||
return $pdf->stream($invoice->nr . '.pdf');
|
||||
return $pdf->download($this->getFilename($invoice) . '.pdf');
|
||||
|
||||
return $pdf->stream($invoice['nr'] . '.pdf');
|
||||
// return $pdf->download($this->getFilename($invoice) . '.pdf');
|
||||
}
|
||||
|
||||
public function exportXml($id)
|
||||
{
|
||||
$invoice = Invoice::with([
|
||||
'customer.contacts' => function ($query) {
|
||||
$query->orderBy('is_primary', 'desc');
|
||||
},
|
||||
'items' => function ($query) {
|
||||
$query->orderBy('position', 'asc');
|
||||
},
|
||||
'customer.paymentTerms'
|
||||
])->findOrFail($id);
|
||||
|
||||
$invoiceArray = $invoice->toArray();
|
||||
$invoiceArray['customer']['payment_terms'] = $invoice->customer->paymentTerms->toArray();
|
||||
unset($invoiceArray['customer']['payment_terms_id']);
|
||||
$invoice = self::single($id);
|
||||
|
||||
// Standard
|
||||
// https://easyfirma.net/e-rechnung/zugferd/bt-felder
|
||||
@@ -166,17 +130,24 @@ public function exportXml($id)
|
||||
// string|null $documentLanguage __BT-X-4, From EXTENDED__ Language indicator. The language code in which the document was written
|
||||
// DateTimeInterface|null $effectiveSpecifiedPeriod __BT-X-6-000, From EXTENDED__ The contractual due date of the invoice
|
||||
$document->setDocumentInformation(
|
||||
$invoice->nr,
|
||||
$invoice['nr'],
|
||||
ZugferdDocumentType::COMMERCIAL_INVOICE,
|
||||
new DateTime($invoice->invoice_date),
|
||||
new DateTime($invoice['invoiceDate']),
|
||||
'EUR',
|
||||
$invoice->title,
|
||||
$invoice['title'],
|
||||
'de',
|
||||
new DateTime($invoice->due_date),
|
||||
new DateTime($invoice['dueDate']),
|
||||
);
|
||||
|
||||
// TODO: start_service_date end_service_date
|
||||
$document->setDocumentBillingPeriod(DateTime::createFromFormat('Ymd', '20250101'), DateTime::createFromFormat('Ymd', '20250131'), '01.01.2025 - 31.01.2025');
|
||||
|
||||
// DateTimeInterface|null $startDate __BT-73, From BASIC WL__ Start of the billing period
|
||||
// DateTimeInterface|null $endDate __BT-74, From BASIC WL__ End of the billing period
|
||||
// string|null $description __BT-X-264, From EXTENDED__ Further information of the billing period (Obsolete)
|
||||
$document->setDocumentBillingPeriod(
|
||||
DateTime::createFromFormat('Y-m-d', $invoice['serviceStartDate']),
|
||||
DateTime::createFromFormat('Y-m-d', $invoice['serviceEndDate']),
|
||||
$invoice['serviceStartDate'] . ' – ' . $invoice['serviceEndDate']
|
||||
);
|
||||
|
||||
|
||||
// Add a payment term
|
||||
@@ -186,11 +157,11 @@ public function exportXml($id)
|
||||
// string|null $directDebitMandateID __BT-89, From BASIC WL__ Unique identifier assigned by the payee to reference the direct debit authorization.
|
||||
// float|null $partialPaymentAmount __BT-X-275, From EXTENDED__ Amount of the partial payment
|
||||
$document->addDocumentPaymentTerm(
|
||||
$invoice->paymentTerms->name == 'prepaid' ?
|
||||
'Vorkasse' : ($invoice->paymentTerms->name == 'on_receipt' ?
|
||||
$invoice['billingData']['paymentTerms']['name'] == 'prepaid' ?
|
||||
'Vorkasse' : ($invoice['billingData']['paymentTerms']['name'] == 'on_receipt' ?
|
||||
'Bei Rechnungserhalt' :
|
||||
$invoice->paymentTerms->days . ' Tage nach Rechnungserhalt'),
|
||||
new DateTime($invoice->due_date)
|
||||
$invoice['billingData']['paymentTerms']['days'] . ' Tage nach Rechnungserhalt'),
|
||||
new DateTime($invoice['dueDate'])
|
||||
);
|
||||
|
||||
|
||||
@@ -243,9 +214,7 @@ public function exportXml($id)
|
||||
// string $name __BT-44, From MINIMUM__ The full name of the buyer
|
||||
// string|null $id __BT-46, From BASIC WL__ An identifier of the buyer. In many systems, buyer identification is key information. Multiple buyer IDs can be assigned or specified. They can be differentiated by using different identification schemes. If no scheme is given, it should be known to the buyer and buyer, e.g. a previously exchanged, seller-assigned identifier of the buyer
|
||||
// string|null $description __BT-X-334, From EXTENDED__ Further legal information about the buyer
|
||||
if ($invoice->customer->company_name && $invoice->customer->customer_nr) {
|
||||
$document->setDocumentBuyer($invoice->customer->company_name, $invoice->customer->customer_nr);
|
||||
}
|
||||
$document->setDocumentBuyer($invoice['billingData']['companyName'], $invoice['customer']['customerNr']);
|
||||
|
||||
// Set contact of the buyer party
|
||||
//
|
||||
@@ -255,12 +224,11 @@ public function exportXml($id)
|
||||
// string|null $contactFaxNo __BT-X-115, From EXTENDED__ A fax number of the contact point
|
||||
// string|null $contactEmailAddress __BT-58, From EN 16931__ An e-mail address of the contact point
|
||||
$document->setDocumentBuyerContact(
|
||||
// TODO: use invoice trade contact field
|
||||
$invoice->customer->contacts[0]->first_name . ' ' . $invoice->customer->contacts[0]->last_name,
|
||||
$invoice['billingData']['contactFirstName'] . ' ' . $invoice['billingData']['contactLastName'],
|
||||
null,
|
||||
$invoice->customer->contacts[0]->phone,
|
||||
null,
|
||||
$invoice->customer->contacts[0]->email
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// Leitweg-ID (nur bei Behörden)
|
||||
@@ -275,21 +243,21 @@ public function exportXml($id)
|
||||
// string|null $country __BT-55, From BASIC WL__ Code used to identify the country. If no tax agent is specified, this is the country in which the sales tax is due. The lists of approved countries are maintained by the EN ISO 3166-1 Maintenance Agency “Codes for the representation of names of countries and their subdivisions”
|
||||
// string|null $subDivision __BT-54, From BASIC WL__ The buyers state
|
||||
$document->setDocumentBuyerAddress(
|
||||
$mappedBillingAddress['line_one'] ?? '',
|
||||
$mappedBillingAddress['line_two'] ?? '',
|
||||
"",
|
||||
$mappedBillingAddress['postal_code'] ?? '',
|
||||
$mappedBillingAddress['city'] ?? '',
|
||||
$mappedBillingAddress['country_code'] ?? ''
|
||||
$invoice['billingData']['billingAddress']['lineOne'] ?? null,
|
||||
$invoice['billingData']['billingAddress']['lineTwo'] ?? null,
|
||||
null,
|
||||
$invoice['billingData']['billingAddress']['postalCode'] ?? null,
|
||||
$invoice['billingData']['billingAddress']['city'] ?? null,
|
||||
$invoice['billingData']['billingAddress']['countryCode'] ?? null
|
||||
);
|
||||
|
||||
foreach ($invoice->items as $item) {
|
||||
$document->addNewPosition($item->position);
|
||||
foreach ($invoice['items'] as $item) {
|
||||
$document->addNewPosition($item['position']);
|
||||
$document->setDocumentPositionProductDetails(
|
||||
$item->title,
|
||||
$item->description
|
||||
$item['title'],
|
||||
$item['description']
|
||||
);
|
||||
$document->setDocumentPositionNetPrice($item->price);
|
||||
$document->setDocumentPositionNetPrice($item['price']);
|
||||
|
||||
// float $billedQuantity __BT-129, From BASIC__ The quantity of individual items (goods or services) billed in the relevant line
|
||||
// string $billedQuantityUnitCode __BT-130, From BASIC__ The unit of measure applicable to the amount billed
|
||||
@@ -297,9 +265,17 @@ public function exportXml($id)
|
||||
// string|null $chargeFreeQuantityUnitCpde __BT-X-46-0, From EXTENDED__ Unit of measure code for the quantity free of charge
|
||||
// float|null $packageQuantity __BT-X-47, From EXTENDED__ Number of packages
|
||||
// string|null $packageQuantityUnitCode __BT-X-47-0, From EXTENDED__ Unit of measure code for number of packages
|
||||
|
||||
// TODO: this needs to be properly mapped. It should probably become a database item, the user can manage
|
||||
$units = [
|
||||
'Stück' => ZugferdUnitCodes::REC20_PIECE,
|
||||
'Stunden' => ZugferdUnitCodes::REC20_HOUR,
|
||||
'Tage' => ZugferdUnitCodes::REC20_WORKING_DAY,
|
||||
'pauschal' => ZugferdUnitCodes::REC20_LUMP_SUM
|
||||
];
|
||||
$document->setDocumentPositionQuantity(
|
||||
$item->quantity,
|
||||
ZugferdUnitCodes::REC20_PIECE
|
||||
$item['quantity'],
|
||||
$units[$item['unit']]
|
||||
);
|
||||
|
||||
$document->addDocumentPositionTax(
|
||||
@@ -308,7 +284,7 @@ public function exportXml($id)
|
||||
19
|
||||
);
|
||||
|
||||
$document->setDocumentPositionLineSummation($item->quantity * $item->price);
|
||||
$document->setDocumentPositionLineSummation($item['quantity'] * $item['price']);
|
||||
}
|
||||
|
||||
// Get the XML content as a string
|
||||
@@ -628,10 +604,10 @@ protected function transliterateString($string)
|
||||
* Generate a filename for the invoice export
|
||||
* Format: YYYY-MM-DD {invoice.nr} {invoice.title}
|
||||
*/
|
||||
protected function getFilename(Invoice $invoice)
|
||||
protected function getFilename($invoice)
|
||||
{
|
||||
$date = \DateTime::createFromFormat("Y-m-d", $invoice->invoice_date);
|
||||
$date = \DateTime::createFromFormat("Y-m-d", $invoice['invoiceDate']);
|
||||
$formattedDate = $date ? $date->format('Y-m-d') : (new DateTime())->format('Y-m-d');
|
||||
return "{$formattedDate} {$invoice->nr} {$invoice->title}";
|
||||
return "{$formattedDate} {$invoice['nr']} {$invoice['title']}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Mail;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Mail\Mailables\Content;
|
||||
use Illuminate\Mail\Mailables\Address;
|
||||
use Illuminate\Mail\Mailables\Envelope;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class Reminder extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*/
|
||||
public function __construct(
|
||||
public array $invoice,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Get the message envelope.
|
||||
*/
|
||||
public function envelope(): Envelope
|
||||
{
|
||||
return new Envelope(
|
||||
// TODO: get from setting
|
||||
from: new Address('buchhaltung@tooloop.de', 'Tooloop Multimedia'),
|
||||
replyTo: [new Address('info@tooloop.de', 'Tooloop Multimedia'),],
|
||||
subject: 'Zahlungserinnerung',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the message content definition.
|
||||
*/
|
||||
public function content(): Content
|
||||
{
|
||||
return new Content(
|
||||
view: 'mail.reminder',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachments for the message.
|
||||
*
|
||||
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
|
||||
*/
|
||||
public function attachments(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user