12-05-2026 Frontend dev
This commit is contained in:
parent
405df0a122
commit
5b8bdf4182
779 changed files with 480564 additions and 6241 deletions
193
app/Services/Billing/LegacyInvoicePdfRenderer.php
Normal file
193
app/Services/Billing/LegacyInvoicePdfRenderer.php
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services\Billing;
|
||||
|
||||
use App\Models\LegacyInvoice;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Str;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class LegacyInvoicePdfRenderer
|
||||
{
|
||||
public function inlineResponse(LegacyInvoice $invoice): Response
|
||||
{
|
||||
$pdf = $this->render($invoice);
|
||||
|
||||
return response($pdf, Response::HTTP_OK, [
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Content-Disposition' => 'inline; filename="'.$this->filename($invoice).'"',
|
||||
'Cache-Control' => 'private, max-age=0, must-revalidate',
|
||||
]);
|
||||
}
|
||||
|
||||
public function downloadResponse(LegacyInvoice $invoice): Response
|
||||
{
|
||||
$pdf = $this->render($invoice);
|
||||
|
||||
return response()->streamDownload(
|
||||
static function () use ($pdf): void {
|
||||
echo $pdf;
|
||||
},
|
||||
$this->filename($invoice),
|
||||
[
|
||||
'Content-Type' => 'application/pdf',
|
||||
'Cache-Control' => 'private, max-age=0, must-revalidate',
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
public function render(LegacyInvoice $invoice): string
|
||||
{
|
||||
$lines = $this->lines($invoice);
|
||||
$content = "BT\n/F1 11 Tf\n50 790 Td\n14 TL\n";
|
||||
|
||||
foreach ($lines as $line) {
|
||||
$content .= '('.$this->escapePdfText($line).") Tj\nT*\n";
|
||||
}
|
||||
|
||||
$content .= "ET\n";
|
||||
|
||||
return $this->buildPdf($content);
|
||||
}
|
||||
|
||||
public function filename(LegacyInvoice $invoice): string
|
||||
{
|
||||
$number = filled($invoice->number) ? (string) $invoice->number : (string) $invoice->legacy_id;
|
||||
$number = preg_replace('/[^A-Za-z0-9._-]/', '-', Str::ascii($number)) ?: (string) $invoice->id;
|
||||
|
||||
$portal = preg_replace('/[^A-Za-z0-9._-]/', '-', Str::ascii($invoice->legacy_portal->label()));
|
||||
|
||||
return "{$portal}-RNr-{$number}.pdf";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
private function lines(LegacyInvoice $invoice): array
|
||||
{
|
||||
$payload = $invoice->pdf_payload ?? [];
|
||||
$billingAddress = data_get($payload, 'billing_address', []);
|
||||
$invoiceData = data_get($payload, 'invoice', $invoice->raw_snapshot ?? []);
|
||||
$invoice->loadMissing('user.profile');
|
||||
|
||||
$isNetto = (bool) data_get($invoiceData, 'is_netto', false);
|
||||
$taxPercent = $this->taxPercent($invoice);
|
||||
$amount = $invoice->total_cents / 100;
|
||||
$netAmount = $isNetto ? $amount : $amount / (1 + ($taxPercent / 100));
|
||||
$taxAmount = $isNetto ? 0 : $amount - $netAmount;
|
||||
$servicePeriodBegin = $this->formatLegacyDate(data_get($invoiceData, 'service_period_begin_date'));
|
||||
$servicePeriodEnd = $this->formatLegacyDate(data_get($invoiceData, 'service_period_end_date'));
|
||||
$serviceName = data_get($payload, 'payment_option_translation.name')
|
||||
?? data_get($payload, 'payment_option.article_number')
|
||||
?? 'Legacy-Leistung';
|
||||
|
||||
return array_values(array_filter([
|
||||
$invoice->legacy_portal->label(),
|
||||
'adametz.media, Kevin Adametz, In der Lake 4, 33739 Bielefeld',
|
||||
'www.businessportal24.com',
|
||||
str_repeat('-', 68),
|
||||
'',
|
||||
'Legacy-Rechnung',
|
||||
'Rechnungsdatum: '.$invoice->invoice_date?->format('d.m.Y'),
|
||||
'',
|
||||
'Rechnungsadresse',
|
||||
data_get($billingAddress, 'name'),
|
||||
data_get($billingAddress, 'title'),
|
||||
data_get($billingAddress, 'address'),
|
||||
trim((string) data_get($billingAddress, 'postal_code').' '.(string) data_get($billingAddress, 'city')),
|
||||
data_get($billingAddress, 'country_name'),
|
||||
$invoice->user?->profile?->tax_id_number ? 'UID-Nr.: '.$invoice->user->profile->tax_id_number : null,
|
||||
'',
|
||||
'Leistung: '.$serviceName.' auf '.$invoice->legacy_portal->label(),
|
||||
$servicePeriodBegin === $servicePeriodEnd
|
||||
? 'Leistungsdatum: '.$servicePeriodBegin
|
||||
: 'Leistungszeitraum: '.$servicePeriodBegin.' - '.$servicePeriodEnd,
|
||||
'Rechnungsnummer: '.($invoice->number ?? '#'.$invoice->legacy_id),
|
||||
'',
|
||||
str_repeat('-', 68),
|
||||
'Rechnungsstellung: '.$invoice->invoice_date?->format('d.m.Y'),
|
||||
str_repeat('-', 68),
|
||||
'Netto: '.$this->formatEuro($netAmount),
|
||||
$isNetto ? null : 'MwSt. '.$taxPercent.'%: '.$this->formatEuro($taxAmount),
|
||||
str_repeat('-', 68),
|
||||
'Betrag: '.$this->formatEuro($amount),
|
||||
str_repeat('-', 68),
|
||||
'',
|
||||
'Zahlart: '.($invoice->payment_method ?? 'n/a'),
|
||||
'Status: '.($invoice->status ?? 'unknown'),
|
||||
$invoice->paid_at ? 'Bezahlt am: '.$invoice->paid_at->format('d.m.Y') : 'Faellig am: '.$invoice->due_date?->format('d.m.Y'),
|
||||
'',
|
||||
'Bitte ueberweisen Sie den Rechnungsbetrag unter Angabe der Rechnungsnummer '.($invoice->number ?? '#'.$invoice->legacy_id).'.',
|
||||
'Bankverbindung: Sparkasse Bielefeld, IBAN DE96 4805 0161 0065 0356 02, BIC SPBIDE3BXXX',
|
||||
$isNetto ? 'Reverse Charge: Steuerschuldnerschaft des Leistungsempfaengers.' : null,
|
||||
'',
|
||||
str_repeat('-', 68),
|
||||
'adametz.media | Tel: +49 5206 7076721 | Mail: info@businessportal24.com',
|
||||
'Steuernummer: 349 / 5001 / 4350 | USt-ID: DE298729654',
|
||||
], fn (mixed $line): bool => $line !== null && $line !== ''));
|
||||
}
|
||||
|
||||
private function taxPercent(LegacyInvoice $invoice): int
|
||||
{
|
||||
$invoiceDate = $invoice->invoice_date;
|
||||
|
||||
if ($invoiceDate && $invoiceDate->betweenIncluded(Carbon::parse('2020-07-01'), Carbon::parse('2020-12-31'))) {
|
||||
return 16;
|
||||
}
|
||||
|
||||
return 19;
|
||||
}
|
||||
|
||||
private function formatLegacyDate(mixed $value): string
|
||||
{
|
||||
if (blank($value) || $value === '0000-00-00' || $value === '0000-00-00 00:00:00') {
|
||||
return 'n/a';
|
||||
}
|
||||
|
||||
return Carbon::parse((string) $value)->format('d.m.Y');
|
||||
}
|
||||
|
||||
private function formatEuro(float $amount): string
|
||||
{
|
||||
return number_format($amount, 2, ',', '.').' EUR';
|
||||
}
|
||||
|
||||
private function buildPdf(string $content): string
|
||||
{
|
||||
$objects = [
|
||||
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n",
|
||||
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n",
|
||||
"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >>\nendobj\n",
|
||||
"4 0 obj\n<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\nendobj\n",
|
||||
"5 0 obj\n<< /Length ".strlen($content)." >>\nstream\n{$content}endstream\nendobj\n",
|
||||
];
|
||||
|
||||
$pdf = "%PDF-1.4\n";
|
||||
$offsets = [0];
|
||||
|
||||
foreach ($objects as $object) {
|
||||
$offsets[] = strlen($pdf);
|
||||
$pdf .= $object;
|
||||
}
|
||||
|
||||
$xrefOffset = strlen($pdf);
|
||||
$pdf .= "xref\n0 ".(count($objects) + 1)."\n";
|
||||
$pdf .= "0000000000 65535 f \n";
|
||||
|
||||
foreach (array_slice($offsets, 1) as $offset) {
|
||||
$pdf .= sprintf("%010d 00000 n \n", $offset);
|
||||
}
|
||||
|
||||
$pdf .= "trailer\n<< /Size ".(count($objects) + 1)." /Root 1 0 R >>\n";
|
||||
$pdf .= "startxref\n{$xrefOffset}\n%%EOF\n";
|
||||
|
||||
return $pdf;
|
||||
}
|
||||
|
||||
private function escapePdfText(string $text): string
|
||||
{
|
||||
$encoded = iconv('UTF-8', 'Windows-1252//TRANSLIT//IGNORE', $text);
|
||||
|
||||
return str_replace(['\\', '(', ')'], ['\\\\', '\(', '\)'], $encoded ?: Str::ascii($text));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue