warehouseoffer v2
This commit is contained in:
@@ -1,81 +1,63 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Template for Warehouse Offer PDF Main Content
|
||||
*
|
||||
* @var \App\Models\WarehouseOfferModel $offer The offer object
|
||||
* @var array $entries Grouped positions array [groupKey => [position1, position2,...]]
|
||||
* @var float $subTotal Calculated subtotal of all positions
|
||||
* @var string $offerNumber Offer number
|
||||
* @var int $offerDate Timestamp of offer creation
|
||||
* @var int|null $validUntilDate Timestamp of offer validity end, or null
|
||||
* @var bool $includeTax Whether to calculate and show VAT
|
||||
* @var float $vatRate The VAT rate (e.g., 0.20 for 20%)
|
||||
* @var string $offerText Additional text from the offer record
|
||||
*
|
||||
* // Bank details are also available but typically used in footer
|
||||
* @var string $bank_iban
|
||||
* @var string $bank_bic
|
||||
* @var string $bank_bank
|
||||
* @var string $bank_owner
|
||||
* All variables are passed from WarehouseOfferController->createPDFAction
|
||||
*/
|
||||
|
||||
// Set filename for download (optional, can also be set in controller)
|
||||
// $this->setReturnValue(['filename' => ($offerNumber ?? $offer->id) . "_Angebot.pdf"]);
|
||||
// --- Helper Functions ---
|
||||
function formatPrice($price, $currency = '€') {
|
||||
return number_format($price, 2, ',', '.') . ' ' . $currency;
|
||||
}
|
||||
|
||||
// --- Text Elements (Simple German Example - Extend for EN like in Order) ---
|
||||
// --- Text Elements ---
|
||||
$texts = [
|
||||
'DE' => [
|
||||
'title' => 'Angebot',
|
||||
'offerNumberLabel' => 'Angebotsnr.:',
|
||||
'offerDateLabel' => 'Datum:',
|
||||
'editorLabel' => 'Sachbearbeiter:',
|
||||
'usageLabel' => 'Zweck:',
|
||||
'customerReferenceLabel' => 'Kundenreferenz:',
|
||||
'validUntilLabel' => 'Gültig bis:',
|
||||
'vatLabel' => 'USt-IdNr.:',
|
||||
'pageLabel' => 'Seite', // For page numbering in content if needed
|
||||
'table' => [
|
||||
'pos' => 'Pos',
|
||||
'article' => 'Artikel / Beschreibung',
|
||||
// 'description' => 'Beschreibung', // Combined with Article
|
||||
'amount' => 'Menge',
|
||||
'unit' => 'Einheit',
|
||||
'unitPrice' => 'Einzelpreis',
|
||||
'totalPrice' => 'Gesamtpreis'
|
||||
],
|
||||
'paymentTerms' => [
|
||||
'net30' => 'Zahlungsbedingungen: 30 Tage netto',
|
||||
'net60' => 'Zahlungsbedingungen: 60 Tage netto',
|
||||
'immediate' => 'Zahlungsbedingungen: Sofort fällig'
|
||||
],
|
||||
'summary' => [
|
||||
'subTotal' => 'Nettobetrag',
|
||||
'vatFormatted' => 'zzgl. {VAT_RATE}% MwSt.', // Placeholder for rate
|
||||
'discount' => 'Rabatt',
|
||||
'vatFormatted' => 'zzgl. {VAT_RATE}% MwSt.',
|
||||
'total' => 'Gesamtbetrag',
|
||||
'currency' => '€' // Currency symbol
|
||||
'alternativeTotal' => 'Summe Alternativpositionen'
|
||||
],
|
||||
'notes' => 'Anmerkungen:',
|
||||
'alternativeHeader' => 'Alternativpositionen',
|
||||
'notes' => 'Anmerkungen & Konditionen',
|
||||
'defaultOfferText' => 'Vielen Dank für Ihre Anfrage. Es gelten unsere Allgemeinen Geschäftsbedingungen.',
|
||||
]
|
||||
// Add 'EN' => [...] section if needed
|
||||
];
|
||||
// Simple language selection (default to DE) - enhance if customer language is known
|
||||
$lang = 'DE';
|
||||
$text = $texts[$lang];
|
||||
$currencySymbol = $text['summary']['currency'];
|
||||
|
||||
// --- Calculations ---
|
||||
$vatAmount = 0;
|
||||
$grandTotal = $subTotal;
|
||||
if ($includeTax) {
|
||||
$vatAmount = $subTotal * $vatRate;
|
||||
$grandTotal = $subTotal + $vatAmount;
|
||||
$discountAmount = 0;
|
||||
$subTotalAfterDiscount = $subTotal;
|
||||
if (isset($offer->totalDiscount) && $offer->totalDiscount > 0) {
|
||||
$discountPercentage = $offer->totalDiscount;
|
||||
$discountAmount = ($subTotal * $discountPercentage) / 100;
|
||||
$subTotalAfterDiscount = $subTotal - $discountAmount;
|
||||
}
|
||||
|
||||
$vatAmount = 0;
|
||||
$grandTotal = $subTotalAfterDiscount;
|
||||
if ($includeTax) {
|
||||
$vatAmount = $subTotalAfterDiscount * $vatRate;
|
||||
$grandTotal = $subTotalAfterDiscount + $vatAmount;
|
||||
}
|
||||
|
||||
// Format dates
|
||||
$formattedOfferDate = date("d.m.Y", $offerDate);
|
||||
$formattedValidUntil = $validUntilDate ? date("d.m.Y", $validUntilDate) : date("d.m.Y", strtotime("+14 days", $offerDate));
|
||||
$formattedValidUntil = date("d.m.Y", strtotime("+14 days", $offerDate));
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
@@ -84,140 +66,41 @@ $formattedValidUntil = $validUntilDate ? date("d.m.Y", $validUntilDate) : date("
|
||||
<title><?= $text['title'] ?> <?= $offerNumber ?></title>
|
||||
<meta charset="utf-8"/>
|
||||
<style>
|
||||
body {
|
||||
font-family: "Open Sans", sans-serif, Verdana;
|
||||
font-size: 10px; /* Slightly smaller base font */
|
||||
color: #333;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #005384; /* Xinon Blue */
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.header-info {
|
||||
margin-bottom: 20px;
|
||||
font-size: 11px;
|
||||
}
|
||||
.header-info table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.header-info td {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
.header-info .label {
|
||||
font-weight: bold;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
width: 100px; /* Fixed width for labels */
|
||||
}
|
||||
body { font-family: "Open Sans", sans-serif, Verdana; font-size: 10px; color: #333; }
|
||||
h1 { text-align: center; color: #005384; font-size: 18px; margin-bottom: 20px; }
|
||||
.header-info table { width: 100%; border-collapse: collapse; font-size: 11px; margin-bottom: 20px; }
|
||||
.header-info td { padding: 2px 5px; }
|
||||
.header-info .label { font-weight: bold; text-align: right; padding-right: 10px; width: 120px; }
|
||||
|
||||
table#positionsTable {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
table#positionsTable th {
|
||||
border-bottom: 2px solid #005384;
|
||||
padding: 8px 4px;
|
||||
text-align: left;
|
||||
background-color: #f2f2f2;
|
||||
font-size: 10px;
|
||||
}
|
||||
table#positionsTable td {
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
padding: 6px 4px;
|
||||
vertical-align: top;
|
||||
}
|
||||
table#positionsTable tr:last-child td {
|
||||
border-bottom: none;
|
||||
}
|
||||
#positionsTable { width: 100%; border-collapse: collapse; margin-top: 15px; margin-bottom: 15px; }
|
||||
#positionsTable th { border-bottom: 2px solid #005384; padding: 8px 4px; text-align: left; background-color: #f2f2f2; font-size: 10px; }
|
||||
#positionsTable td { border-bottom: 1px solid #e1e1e1; padding: 6px 4px; vertical-align: top; }
|
||||
|
||||
/* Column Alignment */
|
||||
table#positionsTable th.pos, table#positionsTable td.pos { text-align: center; width: 30px; }
|
||||
table#positionsTable th.amount, table#positionsTable td.amount { text-align: right; width: 50px;}
|
||||
table#positionsTable th.unit, table#positionsTable td.unit { text-align: center; width: 40px;}
|
||||
table#positionsTable th.price, table#positionsTable td.price { text-align: right; width: 80px;}
|
||||
table#positionsTable th.total, table#positionsTable td.total { text-align: right; width: 90px;}
|
||||
table#positionsTable th.article, table#positionsTable td.article { text-align: left; }
|
||||
#positionsTable th.pos, #positionsTable td.pos { text-align: center; width: 30px; }
|
||||
#positionsTable th.amount, #positionsTable td.amount { text-align: right; width: 50px;}
|
||||
#positionsTable th.unit, #positionsTable td.unit { text-align: center; width: 40px;}
|
||||
#positionsTable th.price, #positionsTable td.price { text-align: right; width: 80px;}
|
||||
#positionsTable th.total, #positionsTable td.total { text-align: right; width: 90px;}
|
||||
|
||||
.position-group-header td {
|
||||
background-color: #e8f0f8; /* Light blue for group headers */
|
||||
font-weight: bold;
|
||||
border-top: 1px solid #ccc;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding: 4px;
|
||||
}
|
||||
.position-group-header td { background-color: #e8f0f8; font-weight: bold; border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; padding: 4px; }
|
||||
.alternative-group-header td { background-color: #fff8e1; font-weight: bold; font-style: italic; }
|
||||
|
||||
.article-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
.article-description {
|
||||
font-size: 9px; /* Smaller font for description */
|
||||
color: #555;
|
||||
padding-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
table#summaryTable {
|
||||
width: 45%; /* Adjust width as needed */
|
||||
float: right; /* Align to the right */
|
||||
border-collapse: collapse;
|
||||
margin-top: 10px;
|
||||
font-size: 11px;
|
||||
}
|
||||
table#summaryTable td {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
table#summaryTable td.label {
|
||||
text-align: right;
|
||||
font-weight: bold;
|
||||
}
|
||||
table#summaryTable td.value {
|
||||
text-align: right;
|
||||
}
|
||||
table#summaryTable tr.grand-total td {
|
||||
border-top: 1px solid #333;
|
||||
border-bottom: 3px double #333; /* Double line for grand total */
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
}
|
||||
table#summaryTable tr.subtotal td {
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.offer-text {
|
||||
margin-top: 30px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #e1e1e1;
|
||||
font-size: 10px;
|
||||
clear: both; /* Ensure it clears the floated summary table */
|
||||
}
|
||||
.offer-text p {
|
||||
margin: 5px 0;
|
||||
}
|
||||
.tax-info {
|
||||
font-style: italic;
|
||||
font-size: 9px;
|
||||
margin-top: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Page break avoidance */
|
||||
tr, .position-group-header {
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
h1, .header-info, table#summaryTable, .offer-text {
|
||||
page-break-before: auto;
|
||||
page-break-after: auto;
|
||||
}
|
||||
table#positionsTable {
|
||||
page-break-inside: auto;
|
||||
}
|
||||
.article-title { font-weight: bold; }
|
||||
.article-description { font-size: 9px; color: #555; padding-left: 10px; margin-top: 2px; }
|
||||
.position-comment { font-size: 9px; color: #444; padding-left: 10px; margin-top: 4px; font-style: italic; }
|
||||
.alternative-position { font-style: italic; color: #555; }
|
||||
|
||||
/* The summary table no longer needs to float */
|
||||
#summaryTable { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 11px; }
|
||||
#summaryTable td { padding: 5px 8px; }
|
||||
#summaryTable td.label { text-align: right; font-weight: bold; }
|
||||
#summaryTable td.value { text-align: right; }
|
||||
#summaryTable tr.grand-total td { border-top: 1px solid #333; border-bottom: 3px double #333; font-weight: bold; font-size: 12px; }
|
||||
#summaryTable tr.subtotal td { border-top: 1px solid #ccc; }
|
||||
|
||||
.footer-container { page-break-inside: avoid; }
|
||||
.offer-text { padding-top: 20px; border-top: 1px solid #eee; margin-top: 20px; font-size: 10px; }
|
||||
.alternative-summary { font-size: 10px; padding: 5px; border: 1px dashed #ccc; background-color: #fafafa; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -234,25 +117,10 @@ $formattedValidUntil = $validUntilDate ? date("d.m.Y", $validUntilDate) : date("
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="label" style="text-align: left"><?= $text['editorLabel'] ?></td>
|
||||
<td><?= $offerEditorName ?></td>
|
||||
<td><?= htmlspecialchars($offerEditorName) ?></td>
|
||||
<td class="label"><?= $text['validUntilLabel'] ?></td>
|
||||
<td><?= $formattedValidUntil ?></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="label" style="text-align: left"><?= $text['usageLabel'] ?></td>
|
||||
<td><?= $offer->purpose ?? 'Keine Angabe' ?></td>
|
||||
<?php if (!empty($offer->customerVAT)) : ?>
|
||||
<td class="label"><?= $text['vatLabel'] ?></td>
|
||||
<td><?= htmlspecialchars($offer->customerVAT) ?></td>
|
||||
<?php endif; ?>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr>
|
||||
<td class="label" style="text-align: left"><?= $text['customerReferenceLabel'] ?></td>
|
||||
<td><?= $offer->reference ?? 'Keine Angabe' ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -271,122 +139,86 @@ $formattedValidUntil = $validUntilDate ? date("d.m.Y", $validUntilDate) : date("
|
||||
<?php
|
||||
$posCounter = 0;
|
||||
foreach ($entries as $groupName => $positions):
|
||||
// Optional: Display group name if it exists
|
||||
$isAlternativeGroup = ($groupName === 'Alternativpositionen');
|
||||
if (!empty($groupName)): ?>
|
||||
<tr class="position-group-header">
|
||||
<td colspan="6"><?= htmlspecialchars($groupName) ?></td>
|
||||
<tr class="position-group-header <?= $isAlternativeGroup ? 'alternative-group-header' : '' ?>">
|
||||
<td colspan="6"><?= htmlspecialchars($isAlternativeGroup ? $text['alternativeHeader'] : $groupName) ?></td>
|
||||
</tr>
|
||||
<?php endif;
|
||||
|
||||
foreach ($positions as $p):
|
||||
if ($p['amount'] == 0) continue;
|
||||
$posCounter++;
|
||||
$rowClass = $isAlternativeGroup ? 'alternative-position' : '';
|
||||
?>
|
||||
<tr>
|
||||
<tr class="<?= $rowClass ?>">
|
||||
<td class="pos"><?= $posCounter ?></td>
|
||||
<td class="article">
|
||||
<div class="article-title">
|
||||
<?php if (!empty($p['articleNumber'])): ?>
|
||||
<strong><?= htmlspecialchars($p['articleNumber']) ?></strong> |
|
||||
<?php endif; ?>
|
||||
|
||||
<?= htmlspecialchars($p['articleText']) ?></div>
|
||||
<?php if (!empty($p['articleDescription'])): ?>
|
||||
<div class="article-description"><?= nl2br(htmlspecialchars($p['articleDescription'])) ?></div>
|
||||
<?php endif; ?>
|
||||
<div class="article-title"><?= htmlspecialchars($p['articleText']) ?></div>
|
||||
<?php if (!empty($p['articleDescription'])): ?><div class="article-description"><?= nl2br(htmlspecialchars($p['articleDescription'])) ?></div><?php endif; ?>
|
||||
<?php if (!empty($p['comment'])): ?><div class="position-comment"><strong>Kommentar:</strong> <?= nl2br(htmlspecialchars($p['comment'])) ?></div><?php endif; ?>
|
||||
</td>
|
||||
<td class="amount"><?= number_format($p['amount'], 2, ',', '.') // Format amount as needed ?></td>
|
||||
<td class="amount"><?= number_format($p['amount'], 2, ',', '.') ?></td>
|
||||
<td class="unit"><?= htmlspecialchars($p['articleUnit']) ?></td>
|
||||
<td class="price"><?= number_format($p['price'], 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
<td class="total"><?= number_format($p['totalPrice'], 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
<td class="price"><?= formatPrice($p['price'], '€') ?></td>
|
||||
<td class="total"><?= formatPrice($p['totalPrice'], '€') ?></td>
|
||||
</tr>
|
||||
<?php endforeach; // End positions loop ?>
|
||||
<?php endforeach; // End entries (groups) loop ?>
|
||||
|
||||
<?php if ($posCounter == 0): // Show message if no positions ?>
|
||||
<tr>
|
||||
<td colspan="6" style="text-align: center; padding: 20px;">Keine Positionen im Angebot enthalten.</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
// --- CALCULATION LOGIC ---
|
||||
|
||||
// Initialize discount variables
|
||||
$discountAmount = 0;
|
||||
$discountPercentage = 0;
|
||||
$subTotalAfterDiscount = $subTotal; // By default, the same as the original subtotal
|
||||
|
||||
// Check if a discount exists and calculate the new amounts
|
||||
if (isset($offer->totalDiscount) && $offer->totalDiscount > 0) {
|
||||
$discountPercentage = $offer->totalDiscount;
|
||||
// Calculate the monetary value of the discount
|
||||
$discountAmount = ($subTotal * $discountPercentage) / 100;
|
||||
// Calculate the subtotal after applying the discount
|
||||
$subTotalAfterDiscount = $subTotal - $discountAmount;
|
||||
}
|
||||
|
||||
// Recalculate VAT and Grand Total based on the potentially discounted subtotal
|
||||
if ($includeTax) {
|
||||
// $vatRate should be a float, e.g., 0.20 for 20%
|
||||
$vatAmount = $subTotalAfterDiscount * $vatRate;
|
||||
$grandTotal = $subTotalAfterDiscount + $vatAmount;
|
||||
} else {
|
||||
$vatAmount = 0; // Ensure VAT is zero if tax is not included
|
||||
$grandTotal = $subTotalAfterDiscount;
|
||||
}
|
||||
|
||||
// --- DISPLAY LOGIC ---
|
||||
?>
|
||||
<table id="summaryTable">
|
||||
<tbody>
|
||||
<tr class="subtotal">
|
||||
<td class="label"><?= $text['summary']['subTotal'] ?>:</td>
|
||||
<td class="value"><?= number_format($subTotal, 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
</tr>
|
||||
|
||||
<?php
|
||||
// Display the discount row only if a discount has been applied
|
||||
if ($discountAmount > 0):
|
||||
// Define a label for the discount. You can add 'discount' to your $text array.
|
||||
$discountLabel = $text['summary']['discount'] ?? 'Rabatt';
|
||||
?>
|
||||
<div class="footer-container">
|
||||
<table style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="label"><?= $discountLabel ?> (<?= number_format($discountPercentage, 0) ?>%):</td>
|
||||
<td class="value">-<?= number_format($discountAmount, 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
<td style="width: 55%; vertical-align: top; padding-right: 20px;">
|
||||
<?php if ($alternativeTotal > 0): ?>
|
||||
<div class="alternative-summary">
|
||||
<strong><?= $text['summary']['alternativeTotal'] ?>:</strong>
|
||||
<span style="float: right;"><?= formatPrice($alternativeTotal, '€') ?></span>
|
||||
<br>
|
||||
<small>(nicht im Gesamtbetrag enthalten)</small>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="width: 45%; vertical-align: top;">
|
||||
<table id="summaryTable">
|
||||
<tbody>
|
||||
<tr class="subtotal">
|
||||
<td class="label">Zwischensumme:</td>
|
||||
<td class="value"><?= formatPrice($subTotal, '€') ?></td>
|
||||
</tr>
|
||||
<?php if ($discountAmount > 0): ?>
|
||||
<tr>
|
||||
<td class="label"><?= $text['summary']['discount'] ?> (<?= number_format($discountPercentage, 0) ?>%):</td>
|
||||
<td class="value">-<?= formatPrice($discountAmount, '€') ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<?php if ($includeTax):
|
||||
$vatLabel = str_replace('{VAT_RATE}', number_format($vatRate * 100, 0), $text['summary']['vatFormatted']);
|
||||
?>
|
||||
<tr>
|
||||
<td class="label"><?= $vatLabel ?>:</td>
|
||||
<td class="value"><?= formatPrice($vatAmount, '€') ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
<tr class="grand-total">
|
||||
<td class="label"><?= $text['summary']['total'] ?>:</td>
|
||||
<td class="value"><?= formatPrice($grandTotal, '€') ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<?php
|
||||
// Display the VAT row if tax is included
|
||||
if ($includeTax):
|
||||
$vatLabel = str_replace('{VAT_RATE}', number_format($vatRate * 100, 0), $text['summary']['vatFormatted']);
|
||||
?>
|
||||
<tr>
|
||||
<td class="label"><?= $vatLabel ?>:</td>
|
||||
<td class="value"><?= number_format($vatAmount, 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
|
||||
<tr class="grand-total">
|
||||
<td class="label"><?= $text['summary']['total'] ?>:</td>
|
||||
<td class="value"><?= number_format($grandTotal, 2, ',', '.') ?> <?= $currencySymbol ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="offer-text">
|
||||
<?php if (!empty($offerText)): ?>
|
||||
<p><strong><?= $text['notes'] ?></strong></p>
|
||||
<div><?= nl2br(htmlspecialchars($offerText)) // Use nl2br to preserve line breaks ?></div>
|
||||
<br>
|
||||
<?php endif; ?>
|
||||
<p><?= $text['defaultOfferText'] // Add default closing text ?></p>
|
||||
<p><?= $text['paymentTerms'][$offer->paymentTerms] ?? $text['paymentTerms']['immediate'] ?></p>
|
||||
<p>Lieferzeit: nach Vereinbarung.</p>
|
||||
<p><?= nl2br(htmlspecialchars($offer->closingText ?? '')) ?></p>
|
||||
<div class="offer-text">
|
||||
<strong><?= $text['notes'] ?></strong>
|
||||
<div><?= nl2br(htmlspecialchars($closingText)) ?></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user