Export um eine einfache Rechnung anhand der Daten eines bestätigten Angebots zu erstellen.
Vorschau
![](https://support.ov2b.de/wp-content/uploads/2024/06/2024-06-05_17-33-46-204x300.png)
Preprozessor
return {
...ctx,
events: ctx.events.map(e => {
// nur das 1. bestätigte Angebot
const confirmedOffer = e?.offersOnEvent?.find(o => o.offerStatus === "CONFIRMED")
let netTotalByVat
if(confirmedOffer){
netTotalByVat = Object.entries(confirmedOffer?.netTotalByVat).map(([vat,_netTotal]) => {
const netTotal = confirmedOffer.discount ? _netTotal - (_netTotal * (confirmedOffer.discount/100)) : _netTotal
const vatTotal = Math.floor(netTotal * (0 + vat / 100))
return {
netTotal,
vatTotal,
vat
}
})
}
return {
...e,
confirmedOffer: {
...confirmedOffer,
netTotalByVat
}
}
})
}
Template
<style>
@page {
size: A4;
margin: 1cm 2cm 0.5cm;
}
.logo {
width: auto;
height: 4cm;
}
.page-container-table {
width: 100%;
}
.page-footer {
position: fixed;
bottom: 1cm;
width: 17cm;
}
.page-footer table {
width: 100%;
font-size: 0.7em;
}
.page-header {
position: fixed;
top: 1cm;
right: 1cm;
}
.page-header,
.page-header-space {
height: 3cm;
}
.page-footer,
.page-footer-space {
height: 2cm;
display: flex;
align-content: end;
}
@media print {
.page-header {
top: 0 !important;
right: 0 !important;
}
.page-footer {
bottom: 0 !important;
}
}
.alert {
background-color: yellow;
font-size: 1.5em;
}
h1 {
font-family: Ubuntu;
text-decoration: none;
}
body {
position: relative;
margin: 0;
padding: 0;
}
body,
h2,
h3,
h4,
p,
th,
td {
font-family: Ubuntu;
}
.page {
margin-top: 2cm;
padding-bottom: 2cm;
page-break-after: auto;
}
.page table {
width: 100%;
border-spacing: 0px;
border-collapse: separate;
}
.page td,
.page th {
text-align: left;
border-bottom: 1px solid black;
padding: 5px 10px;
vertical-align: top;
page-break-inside: avoid;
}
.smallText {
font-size: 0.7rem;
}
.numeric {
text-align: right;
white-space: nowrap;
}
.noWrap {
text-align: right;
white-space: nowrap;
}
</style>
<div class="page-header">
{{#if baseData.images.[0].sizes}}
<img class="logo" src="{{lookup baseData.images.[0].sizes '640'}}" />
{{else}}
<div class="alert">
Kein Logo in Stammdaten hochgeladen
</div>
{{/if}}
</div>
<div class="page-footer">
<table>
<tbody>
<tr>
<td class="smallText">
{{baseData.name}}<br />
{{ baseData.web }}
</td>
<td class="smallText">
{{baseData.addresses.[0].address.street}}<br />
{{baseData.addresses.[0].address.zip}} {{baseData.addresses.[0].address.city}}
</td>
<td class="smallText">
Tel: {{ baseData.phone }} <br />
E-Mail: {{ baseData.email }} <br />
</td>
</tr>
</tbody>
</table>
</div>
<table class="page-container-table">
<thead>
<tr>
<td>
<!--place holder for the fixed-position header-->
<div class="page-header-space"></div>
</td>
</tr>
</thead>
<tbody>
<tr>
<td>
{{#each events}}
<div class="page">
{{ assign 'ansprechpartner' (contactByRole this.contacts "Ansprechpartner:in") }}
{{ assign 'mieter' (contactByRole this.contacts "Mieter:in") }}
{{ assign 'rechnungsnr' (eventInformationByName this.eventInformations "RE-Nr.") }}
{{ assign 'zahlungsziel' (eventInformationByName this.eventInformations "RE-Zahlungsziel") }}
{{#unless rechnungsnr}}
<div class="alert">
Keine Rechnungs-Nr. (Benutzerfeld: RE:Nr.) angegeben
</div>
{{/unless}}
{{#unless zahlungsziel}}
<div class="alert">
Kein Zahlungsziel (Benutzerfeld: RE:Zahlungsziel) angegeben
</div>
{{/unless}}
{{#unless mieter}}
<div class="alert">
Kontakt mit Rolle "Mieter:in" nicht angegeben
</div>
{{/unless}}
{{#unless ansprechpartner}}
<div class="alert">
Kontakt mit Rolle "Ansprechpartner:in" nicht angegeben
</div>
{{/unless}}
{{#unless this.confirmedOffer}}
<div class="alert">
Kein bestätigtes Angebot hinterlegt
</div>
{{/unless}}
<div>
<div class="smallText">
{{baseData.name}} ·
{{baseData.addresses.[0].address.street}} ·
{{baseData.addresses.[0].address.zip}} {{baseData.addresses.[0].address.city}}
</div>
{{#if mieter }}
{{ mieter.displayName }}<br />
{{ansprechpartner.firstName}} {{ansprechpartner.lastName}}<br />
{{ mieter.address.street }}<br />
{{ mieter.address.zip }} {{ mieter.address.city }}
{{/if}}
</div>
<div style="margin-top: 1cm; text-align: right;">Datum: {{ currentDate "P" }}</div>
<div>
<h2>Rechnung {{rechnungsnr}} </h2>
<p>Für Ihre Veranstaltung <b>{{this.displayNames.eventTitleWithArtists}}</b> stellen wir
folgende Kosten in Rechnung:</p>
</div>
{{assign 'anyLineItemHasDiscount' (add (sumBy this.roomsOnOffer 'discount') (sumBy
this.resourcesOnOffer 'discount'))}}
<table>
<thead>
<tr>
<th>Anz.</th>
<th>Einheit</th>
<th>Posten</th>
<th class="numeric">Einzelpreis</th>{{!-- Notiz: Hier keinen Umbruch einfügen --}}{{#if
anyLineItemHasDiscount}}<th class="numeric">Rabatt</th>{{/if}}
<th class="noWrap">Ust. %</th>
<th class="numeric">Gesamt</th>
</tr>
</thead>
<tbody>
{{#each this.confirmedOffer.roomsOnOffer}}
<tr>
<td class="numeric">-</td>
<td>Pauschal</td>
<td>{{this.room}}{{#if this.description}}<br /><em>{{this.description}}</em>{{/if}}</td>
<td class="numeric">{{formatEuro this.price}}</td>{{!-- Notiz: Hier keinen Umbruch
einfügen --}}{{#if anyLineItemHasDiscount}}<td class="numeric">{{#if
this.discount}}{{this.discount}} %{{else}}-{{/if}}</td>{{/if}}
<td class="numeric">{{ this.vat }}</td>
<td class="numeric">{{formatEuro this.netTotal}}</td>
</tr>
{{/each}}
{{#each this.confirmedOffer.resourcesOnOffer}}
<tr>
<td class="numeric">{{this.amount}}</td>
<td>{{#if (isEqual this.unit 'PIECE')}}Stück{{/if}}{{#if (isEqual this.unit
'DAY')}}Tag{{/if}}{{#if (isEqual this.unit 'HOUR')}}Stunde{{/if}}</td>
<td>{{this.resource}}{{#if this.description}}<br />{{this.description}}{{/if}}</td>
<td class="numeric">{{formatEuro this.price}}</td>{{!-- Notiz: Hier keinen Umbruch
einfügen --}}{{#if anyLineItemHasDiscount}}<td class="numeric">{{#if
this.discount}}{{this.discount}} %{{else}}-{{/if}}</td>{{/if}}
<td class="numeric">{{ this.vat }}</td>
<td class="numeric">{{formatEuro this.netTotal}}</td>
</tr>
{{/each}}
{{#if this.confirmedOffer.discount}}
<tr>
<td colspan="{{#if anyLineItemHasDiscount}}6{{else}}5{{/if}}">abzgl.
{{this.confirmedOffer.discount}}% Rabatt</td>
<td class="numeric">-{{formatEuro (multiply this.confirmedOffer.netTotal (divide
this.confirmedOffer.discount 100))}}</td>
</tr>
{{/if}}
<tr>
<th colspan="{{#if anyLineItemHasDiscount}}6{{else}}5{{/if}}">Gesamt netto</th>
<th class="numeric">{{formatEuro this.confirmedOffer.netTotal}}</th>
</tr>
{{#each this.confirmedOffer.netTotalByVat}}
<tr>
<td colspan="{{#if anyLineItemHasDiscount}}6{{else}}5{{/if}}">zzgl. {{this.vat}}% MWSt.
von {{ formatEuro this.netTotal}}</td>
<td class="numeric">{{formatEuro this.vatTotal}}</td>
</tr>
{{/each}}
<tr>
<th colspan="{{#if anyLineItemHasDiscount}}6{{else}}5{{/if}}">Gesamt brutto</th>
<td class="numeric"><strong>{{formatEuro this.confirmedOffer.grossTotal}}<strong></td>
</tr>
</tfoot>
</table>
Wir bitten Sie den Rechnungsbetrag von {{formatEuro confirmedOffer.grossTotal}} bis zum
{{this.zahlungsziel}}
auf das u.a. Konto, mit dem Verwendungszweck "{{this.rechnungsnr}}", zu überweisen.<br /><br />
Wir danken für das entgegengebrachte Vertrauen und begrüßen Sie gerne wieder bei uns.
<h3>Bankverbindung</h3>
IBAN: DE12 7016 9356 0000 1234 12<br />
BIC: GENODEV1EDR
</div>
{{/each}}
</td>
</tr>
</tbody>
<tfoot>
<tr>
<td>
<!--place holder for the fixed-position footer-->
<div class="page-footer-space"></div>
</td>
</tr>
</tfoot>
</table>